from re import U
from ecdsa import util
import pyafl
import json
import struct
from typing import List, Tuple
import os
import subprocess
from typing import List, Dict, Any

from rich.console import Console
from rich.table import Table
from rich.box import ROUNDED
from rich.text import Text
from rich.panel import Panel
import humanize
from datetime import datetime
from utils import PcapGenerator
from enum import Enum, auto
import time
import utils
import signal
from line_profiler import LineProfiler,profile
import random
import copy,os
from typing import List
import numpy as np
import multiprocessing as mp
import threading
import queue
from tqdm import tqdm

from TLSMapper.TLSSUT import *
from TLSMapper.TLSProtocol import *
from utils import Mutator
import hashlib

from natsort import natsorted
from datetime import datetime
import shutil,utils

from natsort import natsorted

import pydot
from collections import defaultdict, deque

def parse_net_address(net_str):
    """
    解析网络地址字符串，返回 (host, port) 元组
    
    参数:
        net_str: 格式为 "tcp://host:port" 或 "tcp://host/port" 的字符串
        
    返回:
        元组 (host, port)，其中 port 是整数
    """
    # 去除协议部分（tcp://）
    if net_str.startswith("tcp://"):
        addr_part = net_str[6:]
    else:
        addr_part = net_str
    
    # 处理 host:port 或 host/port 格式
    if ':' in addr_part:
        host, port = addr_part.split(':')
    elif '/' in addr_part:
        host, port = addr_part.split('/')
    else:
        raise ValueError("Invalid address format")
    
    return (host, int(port))


class FaultCode(Enum):
    """Execution status fault codes"""
    NONE = 0      # 00 - No error
    TMOUT = 1     # 01 - Timeout
    CRASH = 2     # 02 - Crash
    ERROR = 3     # 03 - Error
    NOINST = 4    # 04 - No instrumentation detected
    NOBITS = 5    # 05 - No new bits found


class Stats:
    def __init__(self):


        self.speed = 0
        self.total_tmouts = 0
        self.unique_tmouts = 0
        self.unique_hangs = 0
        self.last_hang_time = 0

        self.total_crashes = 0
        self.unique_crashes = 0

        self.unique_favors = 0
        self.queue_len = 0

        self.favor_paths = 0
        self.total_exec = 0
        self.stage_name = 0

        self.queued_with_cov = 0
        self.current_queued_with_cov = 0

        self.queue_cycle = 0

    def print_all_stats(self):
        """打印所有统计信息，格式化输出"""
        print("\n" + "="*60)
        print("                    FUZZING STATISTICS")
        print("="*60)
        
        # 超时相关统计
        print(f"{'Timeout Statistics:':<25}")
        print(f"  Total Timeouts:     {self.total_tmouts:>10}")
        print(f"  Unique Timeouts:    {self.unique_tmouts:>10}")
        print(f"  Unique Hangs:       {self.unique_hangs:>10}")
        print(f"  Last Hang Time:     {self.last_hang_time:>10}")
        print()
        
        # 崩溃相关统计
        print(f"{'Crash Statistics:':<25}")
        print(f"  Total Crashes:      {self.total_crashes:>10}")
        print(f"  Unique Crashes:     {self.unique_crashes:>10}")
        print()
        
        # 队列相关统计
        print(f"{'Queue Statistics:':<25}")
        print(f"  Queue Length:       {self.queue_len:>10}")
        print(f"  Queue Cycle:        {self.queue_cycle:>10}")
        print(f"  Queued with Cov:    {self.queued_with_cov:>10}")
        print(f"  Current Queued:     {self.current_queued_with_cov:>10}")
        print()
        
        # 执行相关统计
        print(f"{'Execution Statistics:':<25}")
        print(f"  Total Executions:   {self.total_exec:>10}")
        print(f"  Favor Paths:        {self.favor_paths:>10}")
        print(f"  Unique Favors:      {self.unique_favors:>10}")
        print(f"  Stage Name:         {self.stage_name:>10}")
        print()
        
        print("="*60)

    def print_stats_summary(self):
        """打印统计信息摘要，单行格式"""
        print(f"[STATS] Speed: {self.speed:.1f} | "
              f"Exec: {utils.format_count(self.total_exec)} | "
              f"Crashes: {self.unique_crashes} | "
              f"Hangs: {self.unique_hangs} | "
              f"Queue: {self.queue_len} | "
              f"Cycle: {self.queue_cycle} | "
              f"Favors: {self.unique_favors}", flush=True)

    def get_stats_dict(self):
        """返回所有统计信息的字典格式"""
        return {
            'total_tmouts': self.total_tmouts,
            'unique_tmouts': self.unique_tmouts,
            'unique_hangs': self.unique_hangs,
            'last_hang_time': self.last_hang_time,
            'total_crashes': self.total_crashes,
            'unique_crashes': self.unique_crashes,
            'unique_favors': self.unique_favors,
            'queue_len': self.queue_len,
            'favor_paths': self.favor_paths,
            'total_exec': self.total_exec,
            'stage_name': self.stage_name,
            'queued_with_cov': self.queued_with_cov,
            'current_queued_with_cov': self.current_queued_with_cov,
            'queue_cycle': self.queue_cycle
        }



class TestCase:
    def __init__(self,file_path , messages):
        self.file_path = file_path
        self.messages  = messages
        self.cksum =  0       #覆盖率的hash32
        self.bitmap_size = 0
        self.exec_us = 0      #执行时间
        self.file_len =  os.path.getsize(file_path) if file_path  else None 
        self.messages_len = len(messages)
        self.var_behavior = 0 # 多次运行覆盖率是否变化
        
        self.cal_failed = 0
        self.trim_done = 0
        self.was_fuzzed = 0
        self.has_new_cov = 0
        self.favored = 0
        self.depth = 0

        self.trace_mini = 0
        self.trace_mini_hash = 0 # for favor path selection

        self.handicap = 0

        # 计算messages的MD5值

        m = hashlib.md5()
        for msg in messages:
            if isinstance(msg, (bytes, bytearray)):
                m.update(msg)
            elif isinstance(msg, str):
                m.update(msg.encode())

        self.md5 = m.hexdigest()




    def show_status(self):
        """打印测试用例的所有状态信息"""
        print("\n=== TestCase Status ===")
        print(f"File Path: {self.file_path}")
        print(f"File Length: {self.file_len} bytes")
        print(f"Messages Count: {self.messages_len}")
        print(f"Coverage Hash (cksum): {self.cksum}")
        print(f"Bitmap Size: {self.bitmap_size}")
        print(f"Execution Time: {self.exec_us} μs")
        print(f"Variable Behavior: {'Yes' if self.var_behavior else 'No'}")
        # print(f"Trace Mini Length: {len(self.trace_mini)} bytes")
        # print(f"Favored Status: {'Yes' if self.favored else 'No'}")
        # print(f"Redundant Status: {'Yes' if self.fs_redundant else 'No'}")
        print("=======================")


class BaseFuzzer():
    
    def __init__(self, conf_path_or_dict,):

        if isinstance(conf_path_or_dict, dict):
            self.config = conf_path_or_dict
        else:
            with open(conf_path_or_dict, 'r', encoding='utf-8') as f:
                self.config = json.load(f)

        self.init_out_dir()
        

        self.debug()


        self.afl_config = self.config.copy()
        self.afl_config['output_dir'] = os.path.join(self.config['output_dir'], 'afl_out')
        pyafl.parse_args(json.dumps(self.afl_config))
        pyafl.set_up()

        self.exec_tmout = pyafl.get_exec_tmout()
        self.hang_tmout = 1000

        # 初始化test_cases列表
        self.init_test_cases: List[TestCase] = []
        self.stats:Stats = Stats()
        self.__get_test_cases_from_dir()

        self.mutator = Mutator(extras = utils.load_extras_file(self.config['extra']) if 'extra' in self.config else None)

        self.master_dir = os.path.join(self.config['master_dir'], "queue")  if 'master_dir' in self.config else None
        self.master_test_cases_id = 0
        self.master_call_counter = 0  # 用于计数调用次数，每10次执行一次
        
        self.running = True
        signal.signal(signal.SIGINT, self.handle_interrupt)
        
        self.MAP_SIZE = 2 ** 16
        self.top_rated = {}
        self.total_exec = 0

        self.current_test_case = self.init_test_cases[0]
        self.favor_test_cases = []
        self.queue = copy.deepcopy(self.init_test_cases)
        
        # 存储所有测试用例的md5值集合
        self.test_case_md5_set = set()
        # 初始化时将所有初始测试用例的md5添加到集合中
        for test_case in self.init_test_cases:
            self.test_case_md5_set.add(test_case.md5)

        self.mutated_messages = None

        self.score_changed = 0
        self.current_queue_idx = 0


        self.KEEP_UNIQUE_HANG = 500
        self.KEEP_UNIQUE_CRASH = 5000
        self.HAVOC_MAX_MULT = 16
        self.SKIP_TO_NEW_PROB = 99 
        self.HAVOC_MIN = 16 # min havoc times
        self.HAVOC_CYCLES_INIT = 1024

        self.total_cal_us = 0
        self.cal_cycles = 0
        self.total_bitmap_size = 0
        self.total_bitmap_entries = 0

        
        self.pending_favored = 0 # Pending favored paths 
        self.havoc_div = 1

        self.lcg = random.Random()
        # 是否启用splice 变异
        self.splice = False
        

    
        # 初始化日志系统
        self._init_logging()



        self.__get_test_cases_from_queue()
        
        self.hub = 0 # 标识当前test_case是否有趣，0为没有新的覆盖率，1为有新的命中，但是没有新路径 2为新路径
        
        # 初始化消息统计：跟踪每个消息的新覆盖率数量和测试次数
        # 使用消息的 MD5 哈希值作为标识，不区分不同测试用例中的同一消息
        # {message_hash: {'new_cov_count': int, 'test_count': int, 'total_exec_count': int, 'test_case_id': str}}
        self.message_stats = {}
        
        # 初始化累计统计：跟踪每个消息的累计新覆盖率数量和测试次数
        # {message_hash: {'total_new_cov_count': int, 'total_test_count': int, 'total_exec_count': int, 'test_case_id': str}}
        self.message_stats_total = {}
        
        # 上一轮的统计快照（用于计算当前轮的增量）
        self.message_stats_last_cycle = {}
        
        # 初始化测试用例统计：按test_case_id统计
        # {test_case_id: {'test_count': int, 'new_seeds': int, 'exec_count': int}}
        self.test_case_stats = {}  # 当前周期统计
        self.test_case_stats_total = {}  # 累计统计
        self.test_case_stats_last_cycle = {}  # 上一轮统计快照
        
        # 初始化 stats.log 文件
        stats_log_path = os.path.join(self.config.get('output_dir', '.'), 'stats.log')
        self.stats_log_file = open(stats_log_path, 'w', encoding='utf-8')
        # 写入日志头（如果是新文件）
        if self.stats_log_file.tell() == 0:
            self.stats_log_file.write("=" * 80 + "\n")
            self.stats_log_file.write("BaseFuzzer Message Statistics Log\n")
            self.stats_log_file.write("=" * 80 + "\n\n")
            self.stats_log_file.flush()
        
        # 测试seeds目录中的种子
        # self.test_seeds()


    def __get_test_cases_from_queue(self):
        
        queue_to_test = []
        self.is_resume = eval(self.config['is_resume']) if 'is_resume' in self.config else False

        if not self.is_resume:
            return

        if self.is_resume and self.queue_dir and os.path.exists(self.queue_dir):
            # 统计master_dir中所有以.raw结尾的文件
            raw_files = [os.path.join(self.queue_dir, f) for f in os.listdir(self.queue_dir) if f.endswith('.raw')]
            for file_path in natsorted(raw_files):
                messages = self.read_test_case_from_file(file_path)

                test_case = TestCase(messages=messages, file_path=file_path)

                queue_to_test.append(test_case)


            print(f"get {len(raw_files)} test cases from fuzzer {self.queue_dir}")



        self.stats.stage_name = "resume"


        total_iterations = len(queue_to_test) 
        
        with tqdm(total=total_iterations, desc="Resuming test cases", ncols=80) as pbar:
            for test_case in queue_to_test:

                if self.running:
                    fault = self.__run_target_fast(test_case.messages, self.exec_tmout)
                    hub = pyafl.has_new_bit()
                    if hub == 2:
                        self.stats.unique_favors += 1

                    self.test_case_md5_set.add(test_case.md5)
                    
                    self.queue.append(test_case)
                    pbar.update(1)

        self.stats.queue_len = len(self.queue)



        print(f"really get {len(self.queue) - len(self.init_test_cases)} test cases from queue dir {self.queue_dir}")

    def test_seeds(self):
        """
        如果JSON配置中有seeds_dir参数且is_slave不为真，就测试这些seeds
        给出报告，统计有多少有趣的种子，并将它们添加到队列中
        读取方式使用函数read_test_case_from_file
        """
        # 检查是否有seeds_dir参数
        if 'seeds_dir' not in self.config:
            return
        
        # 检查is_slave是否为真
        if self.is_slave:
            return
        
        seeds_dir = self.config['seeds_dir']
        
        # 检查目录是否存在
        if not os.path.exists(seeds_dir):
            print(f"[WARNING] Seeds directory not found: {seeds_dir}")
            return
        
        if not os.path.isdir(seeds_dir):
            print(f"[WARNING] Seeds path is not a directory: {seeds_dir}")
            return
        
        # 获取所有文件（处理.raw和.bin文件）
        seed_files = [os.path.join(seeds_dir, f) for f in os.listdir(seeds_dir) 
                     if os.path.isfile(os.path.join(seeds_dir, f)) and f.endswith(('.raw', '.bin'))]
        
        if not seed_files:
            print(f"[INFO] No seed files found in {seeds_dir}")
            return
        
        print(f"\n{'='*60}")
        print(f"Testing seeds from directory: {seeds_dir}")
        print(f"Total seed files: {len(seed_files)}")
        print(f"{'='*60}\n")
        
        # 统计信息
        total_seeds = len(seed_files)
        interesting_seeds = 0
        new_path_seeds = 0  # hub == 2
        new_bit_seeds = 0   # hub == 1
        failed_seeds = 0
        duplicate_seeds = 0
        
        self.stats.stage_name = "test_seeds"
        
        # 使用tqdm显示进度
        with tqdm(total=total_seeds, desc="Testing seeds", ncols=80) as pbar:
            for file_path in natsorted(seed_files):
                if not self.running:
                    break
                
                try:
                    # 使用read_test_case_from_file读取种子
                    messages = self.read_test_case_from_file(file_path)
                    
                    if not messages:
                        failed_seeds += 1
                        pbar.update(1)
                        continue
                    
                    # 创建临时TestCase用于检查MD5
                    temp_test_case = TestCase(file_path=file_path, messages=messages)
                    
                    # 检查是否已经存在（通过MD5）
                    if temp_test_case.md5 in self.test_case_md5_set:
                        duplicate_seeds += 1
                        pbar.update(1)
                        continue
                    
                    # 运行测试用例
                    fault = self.__run_target_fast(messages, self.exec_tmout)
                    

                    self.save_if_interesting(messages, fault, calibrate=True)
                        
   
                except Exception as e:
                    print(f"\n[ERROR] Failed to test seed {file_path}: {e}")
                    failed_seeds += 1
                
                pbar.update(1)
                
        interesting_seeds = self.stats.queue_len
        new_path_seeds = self.stats.unique_favors
        new_bit_seeds = interesting_seeds - new_path_seeds
        # 生成报告
        print(f"\n{'='*60}")
        print("Seeds Testing Report")
        print(f"{'='*60}")
        if total_seeds > 0:
            print(f"Total seeds tested:      {total_seeds:>6}")
            print(f"Interesting seeds:       {interesting_seeds:>6} ({interesting_seeds*100.0/total_seeds:.1f}%)")
            print(f"  - New paths (hub=2):   {new_path_seeds:>6} ({new_path_seeds*100.0/total_seeds:.1f}%)")
            print(f"  - New bits (hub=1):    {new_bit_seeds:>6} ({new_bit_seeds*100.0/total_seeds:.1f}%)")
            print(f"Duplicate seeds:         {duplicate_seeds:>6} ({duplicate_seeds*100.0/total_seeds:.1f}%)")
            print(f"Failed seeds:            {failed_seeds:>6} ({failed_seeds*100.0/total_seeds:.1f}%)")
            print(f"Seeds added to queue:    {interesting_seeds:>6}")
            print(f"Current queue length:    {len(self.queue):>6}")
        else:
            print("No seeds were tested.")
        print(f"{'='*60}\n")






    def _init_logging(self):
        """初始化日志系统"""

        self.is_slave = eval(self.config['is_slave']) if 'is_slave' in self.config else False


        self.log_path = os.path.join(self.config['output_dir'],'log.txt')

        if self.is_slave:
            # 确保输出目录存在
            os.makedirs(os.path.dirname(self.log_path), exist_ok=True)
            # 打开日志文件（追加模式）
            self.log_file = open(self.log_path, 'a', encoding='utf-8')
            # 保存原始的print函数和stderr
            self._original_print = print
            import sys
            self._original_stderr = sys.stderr
            # 重定向print和stderr到日志文件
            self._redirect_print_to_log()
            self._redirect_stderr_to_log()
            # 设置全局异常处理器
            self._setup_exception_handler()
        else:
            self.log_file = None

    def _truncate_log_if_needed(self):
        """如果日志文件超过1M，删除前面的0.75M，保留后面的0.25M"""
        if not hasattr(self, 'log_path') or not os.path.exists(self.log_path):
            return
        
        try:
            file_size = os.path.getsize(self.log_path)
            # 1M = 1024 * 1024 字节
            max_size = 1024 * 1024  # 1M
            delete_size = 768 * 1024  # 0.75M
            
            if file_size > max_size:
                # 关闭当前文件句柄
                if hasattr(self, 'log_file') and self.log_file:
                    self.log_file.close()
                
                # 读取文件内容
                with open(self.log_path, 'rb') as f:
                    # 跳过前面的0.75M，读取后面的内容
                    f.seek(delete_size)
                    remaining_content = f.read()
                
                # 重新写入文件（只保留后面的内容）
                with open(self.log_path, 'wb') as f:
                    f.write(remaining_content)
                
                # 重新打开文件（追加模式）
                self.log_file = open(self.log_path, 'a', encoding='utf-8')
                
                # 更新重定向的stderr中的log_file引用
                import sys
                if hasattr(sys.stderr, 'log_file'):
                    sys.stderr.log_file = self.log_file
        except Exception as e:
            # 如果截断失败，尝试重新打开文件
            try:
                if not (hasattr(self, 'log_file') and self.log_file and not self.log_file.closed):
                    self.log_file = open(self.log_path, 'a', encoding='utf-8')
            except:
                pass

    def _redirect_print_to_log(self):
        """重定向print函数到日志文件"""

        
        def log_print(*args, **kwargs):
            # 检查并截断日志文件（如果需要）
            self._truncate_log_if_needed()
            
            # 获取当前时间戳
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            # 格式化输出
            message = ' '.join(str(arg) for arg in args)
            # 写入日志文件
            self.log_file.write(f"[{timestamp}] {message}\n")
            self.log_file.flush()  # 确保立即写入
            
            # 同时输出到控制台（如果需要的话）
            if kwargs.get('console', True):
                self._original_print(*args, **kwargs)
        
        # 替换全局print函数
        import builtins
        builtins.print = log_print

    def _redirect_stderr_to_log(self):
        """重定向stderr到日志文件"""
        import sys
        
        class StderrLogger:
            def __init__(self, log_file, original_stderr, fuzzer_instance):
                self.log_file = log_file
                self.original_stderr = original_stderr
                self._fuzzer_instance = fuzzer_instance
            
            def write(self, message):
                if message.strip():  # 只记录非空消息
                    # 检查并截断日志文件（如果需要）
                    if hasattr(self._fuzzer_instance, '_truncate_log_if_needed'):
                        self._fuzzer_instance._truncate_log_if_needed()
                    # 使用fuzzer_instance的log_file，确保使用最新的文件句柄
                    log_file = getattr(self._fuzzer_instance, 'log_file', self.log_file)
                    if log_file:
                        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        log_file.write(f"[{timestamp}] [STDERR] {message}")
                        log_file.flush()
                # 同时输出到原始stderr
                self.original_stderr.write(message)
                self.original_stderr.flush()
            
            def flush(self):
                log_file = getattr(self._fuzzer_instance, 'log_file', self.log_file)
                if log_file:
                    log_file.flush()
                self.original_stderr.flush()
            
            def __getattr__(self, name):
                return getattr(self.original_stderr, name)
        
        # 替换stderr
        sys.stderr = StderrLogger(self.log_file, self._original_stderr, self)

    def _setup_exception_handler(self):
        """设置全局异常处理器"""
        import sys
        import traceback
        
        def exception_handler(exc_type, exc_value, exc_traceback):
            if self.is_slave and hasattr(self, 'log_file'):
                # 检查并截断日志文件（如果需要）
                self._truncate_log_if_needed()
                timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                error_msg = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
                self.log_file.write(f"[{timestamp}] [EXCEPTION] {error_msg}")
                self.log_file.flush()
            
            # 调用原始的异常处理器
            sys.__excepthook__(exc_type, exc_value, exc_traceback)
        
        # 设置全局异常处理器
        sys.excepthook = exception_handler

    def _restore_print(self):
        """恢复原始的print函数"""
        if self.is_slave and hasattr(self, '_original_print'):
            import builtins
            builtins.print = self._original_print

    def _restore_stderr(self):
        """恢复原始的stderr"""
        if self.is_slave and hasattr(self, '_original_stderr'):
            import sys
            sys.stderr = self._original_stderr

    def _restore_exception_handler(self):
        """恢复原始的异常处理器"""
        if self.is_slave:
            import sys
            sys.excepthook = sys.__excepthook__

    def close_logging(self):
        """关闭日志系统"""
        if hasattr(self, 'is_slave') and self.is_slave and hasattr(self, 'log_file') and self.log_file:
            self._restore_print()
            self._restore_stderr()
            self._restore_exception_handler()
            self.log_file.close()

    def __del__(self):
        """析构函数，确保日志文件被正确关闭"""
        self.close_logging()

    def log_info(self, message):
        """记录信息日志"""
        if self.is_slave:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.log_file.write(f"[{timestamp}] [INFO] {message}\n")
            self.log_file.flush()
        else:
            print(f"[INFO] {message}")

    def log_warning(self, message):
        """记录警告日志"""
        if self.is_slave:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.log_file.write(f"[{timestamp}] [WARNING] {message}\n")
            self.log_file.flush()
        else:
            print(f"[WARNING] {message}")

    def log_error(self, message):
        """记录错误日志"""
        if self.is_slave:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.log_file.write(f"[{timestamp}] [ERROR] {message}\n")
            self.log_file.flush()
        else:
            print(f"[ERROR] {message}")

    def log_debug(self, message):
        """记录调试日志"""
        if self.is_slave:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.log_file.write(f"[{timestamp}] [DEBUG] {message}\n")
            self.log_file.flush()
        else:
            print(f"[DEBUG] {message}")

    def log_performance(self, message):
        """记录性能日志"""
        if self.is_slave:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.log_file.write(f"[{timestamp}] [PERF] {message}\n")
            self.log_file.flush()
        else:
            print(f"[PERF] {message}")

    def log_exception(self, exc_type, exc_value, exc_traceback):
        """记录异常信息"""
        if self.is_slave:
            import traceback
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            error_msg = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
            self.log_file.write(f"[{timestamp}] [EXCEPTION] {error_msg}")
            self.log_file.flush()
        else:
            import traceback
            print(f"[EXCEPTION] {''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))}")

    def init_out_dir(self):
        out_parent_dir = self.config['output_dir']
        self.favor_test_cases_dir = os.path.join(out_parent_dir,'favor_test_cases')
        self.queue_dir = os.path.join(out_parent_dir,'queue')
        self.origin_queue_dir = os.path.join(out_parent_dir,'origin_queue')
        self.crash_test_cases_dir = os.path.join(out_parent_dir,'crash_test_cases')
        self.tmout_test_cases_dir = os.path.join(out_parent_dir,'tmout_test_cases')

        os.makedirs(self.favor_test_cases_dir,exist_ok=True)
        os.makedirs(self.crash_test_cases_dir,exist_ok=True)
        os.makedirs(self.tmout_test_cases_dir,exist_ok=True)
        os.makedirs(self.queue_dir,exist_ok=True)
        os.makedirs(self.origin_queue_dir,exist_ok=True)


        
    def __get_test_cases_from_dir(self) -> None:

        input_dir = self.config["input_dir"]
        
        # 确保输入目录存在
        if not os.path.isdir(input_dir):
            raise FileNotFoundError(f"Input directory not found: {input_dir}")
        
        # 遍历目录下所有文件
        for file_name in os.listdir(input_dir):
            file_path = os.path.join(input_dir, file_name)
            
            # 跳过目录，只处理文件
            if not os.path.isfile(file_path):
                continue
            
            # 读取文件内容
            with open(file_path, 'rb') as f:
                file_content = f.read()
            
            # 根据协议类型提取消息
            if self.config['protocol'] == "TLS":
                messages = utils.extract_requests_tls(file_content)
                ### for test
                # messages = [bytes(100) for _ in range(5)] 
            else:
                # 其他协议的处理可以在这里扩展
                messages = [bytearray(file_content)]  # 默认整个文件作为一个消息
            
            # 保存到test_cases列表
            self.init_test_cases.append(TestCase(file_path,messages))

    
    def get_init_test_cases(self):
        return self.init_test_cases

    def print_init_test_cases(self) -> None:
        """
        以美观的表格格式打印所有测试用例信息
        
        显示内容包括:
        - 文件序号
        - 文件名
        - 文件大小
        - 消息数量
        - 首条消息预览
        """
        if not hasattr(self, 'test_cases') or not self.init_test_cases:
            print("[yellow]⚠️ 没有可用的测试用例[/yellow]")
            return

        console = Console()
        
        # 创建主表格
        table = Table(
            title="[bold cyan]测试用例概览[/bold cyan]",
            box=ROUNDED,
            header_style="bold magenta",
            title_style="bold",
            show_header=True,
            expand=True
        )
        
        # 添加列
        table.add_column("#", justify="right", style="cyan", no_wrap=True)
        table.add_column("文件名", style="green", min_width=20)
        table.add_column("大小", justify="right", style="blue")
        table.add_column("消息数", justify="right", style="yellow")
        table.add_column("首条消息预览", style="dim", min_width=30)

        # 添加数据行
        for idx, case in enumerate(self.init_test_cases, 1):
            file_size = os.path.getsize(case.file_path)
            first_msg = case.messages[0] if case.messages else b''
            
            # 格式化消息预览 (显示前16字节的hex)
            preview = first_msg[:16].hex(' ') + ("..." if len(first_msg) > 16 else "")
            
            table.add_row(
                str(idx),
                case.file_path,
                humanize.naturalsize(file_size),
                str(len(case.messages)),
                preview
            )

        # 创建统计面板
        total_files = len(self.init_test_cases)
        total_messages = sum(len(case.messages) for case in self.init_test_cases)
        stats = Panel(
            f"[b]文件总数:[/b] {total_files}\n"
            f"[b]消息总数:[/b] {total_messages}\n"
            f"[b]输入目录:[/b] {self.config['input_dir']}",
            title="[bold]统计信息[/bold]",
            border_style="blue",
            width=50
        )

        # 打印输出
        console.print(stats)
        console.print(table)
        
        # 添加提示信息
        console.print(
            "[dim]提示: 使用 [bold]get_test_case_detail(index)[/bold] 查看特定用例的详细信息[/dim]",
            justify="center"
        )

    def get_test_case_detail(self, index: int) -> None:
        """
        打印指定测试用例的详细信息
        """
        if not 0 <= index-1 < len(self.init_test_cases):
            print("[red]❌ 无效的索引[/red]")
            return
        
        case = self.init_test_cases[index-1]
        console = Console()
        
        # 创建详细面板
        info = Panel(
            f"[b]路径:[/b] {case.file_path}\n"
            f"[b]大小:[/b] {humanize.naturalsize(os.path.getsize(case.file_path))}\n"
            f"[b]消息数:[/b] {len(case.messages)}",
            title=f"[bold]测试用例 #{index} 详情[/bold]",
            border_style="green"
        )
        
        # 创建消息表格
        msg_table = Table(
            title="[bold]消息列表[/bold]",
            box=ROUNDED,
            show_header=True,
            header_style="bold blue"
        )
        msg_table.add_column("#", style="cyan")
        msg_table.add_column("类型", style="magenta")
        msg_table.add_column("长度", justify="right")
        msg_table.add_column("内容预览", style="dim")
        
        for i, msg in enumerate(case.messages, 1):
            msg_type = "TLS" if self.config['protocol'] == "TLS" else "RAW"
            preview = msg[:16].hex(' ') + ("..." if len(msg) > 16 else "")
            msg_table.add_row(
                str(i),
                msg_type,
                humanize.naturalsize(len(msg)),
                preview
            )
        
        # 打印输出
        console.print(info)
        console.print(msg_table)

    @profile 
    def run_target(self,test_case):

        self.stats.total_exec += 1
        messages = test_case.messages
        timeout = self.exec_tmout  
        response = []

        pyafl.pre_run_target(timeout)


        response.append(pyafl.get_response_buff())
        

        for msg in messages:
            
            pyafl.run_target(bytes(msg))
            response.append(pyafl.get_response_buff())

        
        # pyafl.run_target(messages[0])
        # response.append(pyafl.get_response_buff())
        # pyafl.run_target(messages[1])
        # response.append(pyafl.get_response_buff())
        # pyafl.run_target(messages[2])
        # response.append(pyafl.get_response_buff())
        # pyafl.run_target(messages[3])
        # response.append(pyafl.get_response_buff())
        # pyafl.run_target(messages[4])
        # response.append(pyafl.get_response_buff())


        pyafl.post_run_target(timeout)

        # response.append(pyafl.get_response_buff())
        return messages,response



    def run_target_fast(self, messages, timeout):

        self.stats.total_exec += 1

        response = []

        pyafl.pre_run_target(timeout)
        response.append(pyafl.get_response_buff())
    
        for msg in messages:
            pyafl.run_target(bytes(msg))
            response.append(pyafl.get_response_buff())

        fault = pyafl.post_run_target(timeout)

        return fault

    def __run_target_fast(self, messages, timeout):

        self.stats.total_exec += 1

        response = []

        pyafl.pre_run_target(timeout)
        response.append(pyafl.get_response_buff())
    
        for msg in messages:
            pyafl.run_target(bytes(msg))
            response.append(pyafl.get_response_buff())

        fault = pyafl.post_run_target(timeout)

        return fault

    def send_test_case(self, messages, timeout=None):
        """
        通过socket连接直接发送测试数据到目标服务器
        
        Args:
            messages: 要发送的消息列表，每个元素是bytes类型
            timeout: 超时时间（秒），如果为None则使用配置中的exec_tmout
            
        Returns:
            tuple: (fault_code, response_data)
                - fault_code: 执行结果代码 (FaultCode枚举)
                - response_data: 接收到的响应数据列表
        """
        import socket
        import time
        
        if timeout is None:
            timeout = self.exec_tmout / 1000.0  # 转换为秒
        
        # 解析网络地址
        if 'use_net' not in self.config:
            raise ValueError("配置中缺少 'use_net' 字段")
        
        host, port = parse_net_address(self.config['use_net'])
        
        # 统计执行次数
        self.stats.total_exec += 1
        
        fault_code = FaultCode.NONE
        response_data = []
    
        # 创建socket连接
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(timeout)
        
        # 连接到目标服务器
        sock.connect((host, port))
        
        # 发送所有消息
        for msg in messages:
            if isinstance(msg, (list, tuple)):
                # 如果消息是列表或元组，转换为bytes
                msg_bytes = bytes(msg)
            else:
                # 确保是bytes类型
                msg_bytes = bytes(msg) if not isinstance(msg, bytes) else msg
            
            # 发送消息
            sock.sendall(msg_bytes)
            time.sleep(0.1)



        
        return fault_code, response_data


    def profile_run_target(self,test_case):

        profiler = LineProfiler()
        profiler.add_function(self.run_target)  # 添加要分析的函数

        # 运行分析
        profiler.enable_by_count()
        result = self.run_target(test_case)
        profiler.disable_by_count()

        # 打印结果
        profiler.print_stats(output_unit=1e-3)




    def debug(self):
        pyafl.debug()
    
    def clear(self):
        pyafl.clear()

    def calibrate_case(self,test_case:TestCase,handicap = 0):

        first_run = not test_case.cksum
        stage_max = 7
        first_trace = None
        fault = FaultCode.NONE

        last_stage_name = self.stats.stage_name
        self.stats.stage_name = "calibration"

        start_time_us = utils.get_cur_time_us()


        for i in range(stage_max):

            self.__run_target_fast(test_case.messages,self.exec_tmout)

            if not i and not pyafl.trace_bytes_count():
                fault = FaultCode.NOINST
                return fault

            cksum = pyafl.trace_hash32()

            if test_case.cksum != cksum:

                # 如果test_case.cksum有值且和这次运行不相等，说明两次运行代码覆盖不一致
                if test_case.cksum:
                    test_case.var_behavior = 1

                
                # 说明是第一次运行
                else:
                    test_case.cksum = cksum

  
        stop_time_us = utils.get_cur_time_us()

        self.total_cal_us += stop_time_us - start_time_us
        self.cal_cycles += stage_max

        test_case.exec_us = (stop_time_us - start_time_us) / stage_max


        test_case.bitmap_size = pyafl.trace_bytes_count()
        test_case.handicap = handicap
 

        test_case.trace_mini_hash = pyafl.trace_min_hash32()



        self.total_bitmap_size += test_case.bitmap_size
        self.total_bitmap_entries += 1


        self.stats.stage_name = last_stage_name

        return fault


    def cull_queue(self,test_case:TestCase):
        flags = 0
        if test_case.trace_mini_hash not in self.top_rated:
            self.top_rated[test_case.trace_mini_hash] = test_case
            flags = 1

            
            
        else:
            pre = self.top_rated[test_case.trace_mini_hash]
            favor_factor = test_case.exec_us * test_case.messages_len
            if favor_factor < pre.exec_us * pre.messages_len:
                self.top_rated[test_case.trace_mini_hash] = test_case
                flags = 1
        
        if flags:
            # self.score_changed = 1
            self.stats.favor_paths += 1
            test_case.favored = 1
            if not test_case.was_fuzzed:
                self.pending_favored += 1
        
        return flags
            








    def perform_dry_run(self):

        for test_case in self.init_test_cases:
            print(f"Attempting dry run with {test_case.file_path}")
            Fault = self.calibrate_case(test_case,0)
            if(test_case.var_behavior):
                print("warning: Instrumentation output varies across runs.")


            # test_case.show_status()
    

    def handle_interrupt(self, signum, frame):
        print("\n[!] 检测到中断信号，正在停止...")
        self.running = False
        # 关闭统计日志文件
        if hasattr(self, 'close_stats_log'):
            self.close_stats_log()
        sys.exit(0)
    
    def _get_message_hash(self, message):
        """
        计算消息的 MD5 哈希值作为唯一标识
        """
        m = hashlib.md5()
        if isinstance(message, (bytes, bytearray)):
            m.update(message)
        elif isinstance(message, str):
            m.update(message.encode())
        else:
            m.update(str(message).encode())
        return m.hexdigest()
    
    def _get_test_case_id(self, test_case):
        """
        获取测试用例的ID（用于标识）
        优先使用文件路径，如果没有则使用MD5
        """
        if isinstance(test_case, TestCase):
            if test_case.file_path:
                # 使用文件名作为ID
                import os
                return os.path.basename(test_case.file_path)
            else:
                # 使用MD5的前8个字符
                return test_case.md5[:8] if hasattr(test_case, 'md5') else 'unknown'
        return 'unknown'
    
    def _get_message_identifier(self, message_hash):
        """
        获取消息的简短标识符（用于日志显示）
        返回：消息长度（如果可用）
        """
        # 尝试从当前测试用例中获取消息长度
        if isinstance(self.current_test_case, TestCase):
            for msg in self.current_test_case.messages:
                if self._get_message_hash(msg) == message_hash:
                    msg_len = len(msg) if isinstance(msg, (bytes, bytearray)) else len(str(msg))
                    return f"{msg_len}B"
        
        # 如果找不到，返回哈希值的前8位
        return f"hash:{message_hash[:8]}"
    
    def _format_exec_count(self, count):
        """
        格式化执行次数，添加K、M单位
        """
        if count >= 1000000:
            return f"{count/1000000:.1f}M"
        elif count >= 1000:
            return f"{count/1000:.1f}K"
        else:
            return str(count)
    
    def _log_message_stats(self):
        """
        输出每个测试用例的统计信息到 stats.log 文件
        包括：1. 当前周期的统计 2. 累计统计
        使用test_case_stats字典，按test_case_id统计
        """
        if not hasattr(self, 'stats_log_file') or not hasattr(self, 'test_case_stats'):
            return
        
        try:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.stats_log_file.write(f"\n[Cycle {self.stats.queue_cycle}] Statistics at {timestamp}\n")
            self.stats_log_file.write(f"Total Executions: {self.stats.total_exec}\n")
            self.stats_log_file.write("=" * 80 + "\n")
            
            # 从队列目录获取所有test_case，确保所有test_case都被统计
            all_test_case_ids = set()
            if hasattr(self, 'queue_dir') and self.queue_dir and os.path.exists(self.queue_dir):
                raw_files = [f for f in os.listdir(self.queue_dir) if f.endswith('.raw')]
                for f in natsorted(raw_files):
                    all_test_case_ids.add(f)
            
            # 从test_case_stats中获取已有的test_case_id
            for test_case_id in self.test_case_stats.keys():
                all_test_case_ids.add(test_case_id)
            
            # ========== 当前周期的统计 ==========
            self.stats_log_file.write("\n--- Current Cycle Statistics ---\n")
            self.stats_log_file.write("-" * 90 + "\n")
            self.stats_log_file.write(f"{'Test Case ID':<30} {'Total Tests':<15} {'Total New Seeds':<15} {'Total Exec':<15}\n")
            self.stats_log_file.write("-" * 90 + "\n")
            
            # 计算当前周期的增量（相对于上一轮）
            cycle_stats = {}
            for test_case_id in all_test_case_ids:
                current_stats = self.test_case_stats.get(test_case_id, {'test_count': 0, 'new_seeds': 0, 'exec_count': 0})
                last_stats = self.test_case_stats_last_cycle.get(test_case_id, {'test_count': 0, 'new_seeds': 0, 'exec_count': 0})
                
                # 计算增量，如果为负数则设为0（说明该测试用例在新周期中还没有活动）
                test_count_diff = current_stats['test_count'] - last_stats['test_count']
                new_seeds_diff = current_stats['new_seeds'] - last_stats['new_seeds']
                exec_count_diff = current_stats['exec_count'] - last_stats['exec_count']
                
                cycle_stats[test_case_id] = {
                    'test_count': max(0, test_count_diff),
                    'new_seeds': max(0, new_seeds_diff),
                    'exec_count': max(0, exec_count_diff)
                }
            
            # 按test_case_id排序输出当前周期统计
            total_cycle_test = 0
            total_cycle_new_seeds = 0
            total_cycle_exec = 0
            
            for test_case_id in natsorted(cycle_stats.keys()):
                stats = cycle_stats[test_case_id]
                exec_str = self._format_exec_count(stats['exec_count'])
                self.stats_log_file.write(
                    f"{test_case_id:<30} {stats['test_count']:<15} {stats['new_seeds']:<15} {exec_str:<15}\n"
                )
                total_cycle_test += stats['test_count']
                total_cycle_new_seeds += stats['new_seeds']
                total_cycle_exec += stats['exec_count']
            
            # 当前周期总计
            self.stats_log_file.write("-" * 90 + "\n")
            exec_str = self._format_exec_count(total_cycle_exec)
            self.stats_log_file.write(
                f"{'CYCLE TOTAL':<30} {total_cycle_test:<15} {total_cycle_new_seeds:<15} {exec_str:<15}\n"
            )
            
            # ========== 累计统计 ==========
            self.stats_log_file.write("\n--- Total Statistics (All Cycles) ---\n")
            self.stats_log_file.write("-" * 90 + "\n")
            self.stats_log_file.write(f"{'Test Case ID':<30} {'Total Tests':<15} {'Total New Seeds':<15} {'Total Exec':<15}\n")
            self.stats_log_file.write("-" * 90 + "\n")
            
            # 按test_case_id排序输出累计统计
            total_all_test = 0
            total_all_new_seeds = 0
            total_all_exec = 0
            
            for test_case_id in natsorted(all_test_case_ids):
                stats = self.test_case_stats_total.get(test_case_id, {'test_count': 0, 'new_seeds': 0, 'exec_count': 0})
                exec_str = self._format_exec_count(stats['exec_count'])
                self.stats_log_file.write(
                    f"{test_case_id:<30} {stats['test_count']:<15} {stats['new_seeds']:<15} {exec_str:<15}\n"
                )
                total_all_test += stats['test_count']
                total_all_new_seeds += stats['new_seeds']
                total_all_exec += stats['exec_count']
            
            # 累计总计
            self.stats_log_file.write("-" * 90 + "\n")
            exec_str = self._format_exec_count(total_all_exec)
            self.stats_log_file.write(
                f"{'TOTAL (ALL)':<30} {total_all_test:<15} {total_all_new_seeds:<15} {exec_str:<15}\n"
            )
            
            # ========== 总体统计摘要 ==========
            self.stats_log_file.write("\n--- Overall Summary ---\n")
            self.stats_log_file.write("-" * 100 + "\n")
            self.stats_log_file.write(f"Total Executions: {self.stats.total_exec}\n")
            self.stats_log_file.write(f"Total Queue Size: {self.stats.queue_len}\n")
            self.stats_log_file.write(f"Total Test Cases: {len(all_test_case_ids)}\n")
            self.stats_log_file.write(f"Total Test Count (All Test Cases): {total_all_test}\n")
            self.stats_log_file.write(f"Total New Seeds (All Test Cases): {total_all_new_seeds}\n")
            self.stats_log_file.write("-" * 100 + "\n")
            
            self.stats_log_file.write("=" * 80 + "\n")
            self.stats_log_file.flush()  # 确保立即写入文件
            
            # 更新上一轮的统计快照
            # 对于所有测试用例，更新快照（包括那些在新周期中没有活动的测试用例）
            for test_case_id in all_test_case_ids:
                current_stats = self.test_case_stats.get(test_case_id, {'test_count': 0, 'new_seeds': 0, 'exec_count': 0})
                self.test_case_stats_last_cycle[test_case_id] = {
                    'test_count': current_stats['test_count'],
                    'new_seeds': current_stats['new_seeds'],
                    'exec_count': current_stats['exec_count']
                }
            
        except Exception as e:
            print(f"Warning: Failed to write stats log: {e}")
    
    def close_stats_log(self):
        """
        关闭 stats.log 文件
        """
        if hasattr(self, 'stats_log_file') and self.stats_log_file:
            try:
                # 写入结束标记
                self.stats_log_file.write("\n" + "=" * 80 + "\n")
                self.stats_log_file.write("Fuzzing session ended.\n")
                self.stats_log_file.write("=" * 80 + "\n")
                self.stats_log_file.close()
            except Exception as e:
                print(f"Warning: Failed to close stats log: {e}")

    def calculate_score(self,test_case:TestCase):
        avg_exec_us = self.total_cal_us / self.cal_cycles if self.cal_cycles else 0
        avg_bitmap_size = self.total_bitmap_size / self.total_bitmap_entries if self.total_bitmap_entries else 0

        exec_us = test_case.exec_us
        bitmap_size = test_case.bitmap_size

        perf_score = (
            10 if exec_us * 0.1 > avg_exec_us else
            25 if exec_us * 0.25 > avg_exec_us else
            50 if exec_us * 0.5 > avg_exec_us else
            75 if exec_us * 0.75 > avg_exec_us else
            300 if exec_us * 4 < avg_exec_us else
            200 if exec_us * 3 < avg_exec_us else
            150 if exec_us * 2 < avg_exec_us else
            100
        )

        perf_score *= (
            3 if bitmap_size * 0.3 > avg_bitmap_size else
            2 if bitmap_size * 0.5 > avg_bitmap_size else
            1.5 if bitmap_size * 0.75 > avg_bitmap_size else
            0.25 if bitmap_size * 3 < avg_bitmap_size else
            0.5 if bitmap_size * 2 < avg_bitmap_size else
            0.75 if bitmap_size * 1.5 < avg_bitmap_size else
            1
        )

        if test_case.handicap >= 4:
            perf_score *= 4
            test_case.handicap -= 4
        elif test_case.handicap:
            perf_score *= 2
            test_case.handicap -= 1
        
        if perf_score > self.HAVOC_MAX_MULT * 100:
            perf_score = self.HAVOC_MAX_MULT * 100
        
        perf_score /= (
            10 if avg_exec_us > 50000 else
            5 if avg_exec_us > 20000 else
            2 if avg_exec_us > 10000 else
            1
        )

        return perf_score



    def splice_msgs(self, msg1: List[bytearray]):
            """
            跨消息字节级拼接：
            1. 将 msg1 和 msg2 视为全局字节流
            2. 找出全局字节差异区间 [start_byte, end_byte]
            3. 在差异区间内随机选一个全局拼接点
            4. 生成新序列：msg1 前半 + msg2 后半（在字节级别）
            """
            if not msg1 or len(self.queue) < 2:
                return

            # 预计算 msg1 的总字节和消息边界
            total1 = bytearray()
            offsets1 = [0]  # 每个消息的起始偏移
            for m in msg1:
                total1.extend(m)
                offsets1.append(len(total1))

            MAX_RETRIES = 15
            attempts = 0

            while attempts < MAX_RETRIES:
                attempts += 1
                msg2 = self.queue[self.lcg.randint(0,len(self.queue)-1)].messages
                if not msg2:
                    continue

                # 构建 msg2 的总字节流
                total2 = bytearray()
                offsets2 = [0]
                for m in msg2:
                    total2.extend(m)
                    offsets2.append(len(total2))

                total_len = max(len(total1), len(total2))
                if total_len == 0:
                    continue

                # === Step 1: 找全局字节差异起点 ===
                start_byte = 0
                min_len = min(len(total1), len(total2))
                while start_byte < min_len and total1[start_byte] == total2[start_byte]:
                    start_byte += 1

                if start_byte == min_len and len(total1) == len(total2):
                    continue  # 完全相同

                # === Step 2: 找全局字节差异终点 ===
                end_byte = total_len - 1
                while end_byte >= start_byte:
                    b1 = total1[end_byte] if end_byte < len(total1) else None
                    b2 = total2[end_byte] if end_byte < len(total2) else None
                    if b1 != b2:
                        break
                    end_byte -= 1

                if end_byte < start_byte:
                    continue

                # 差异字节数至少为 2？
                if end_byte - start_byte < 1:
                    continue

                # === Step 3: 在 [start_byte, end_byte] 内随机选拼接点 ===
                splice_byte = start_byte + self.lcg.randint(0, end_byte - start_byte-1)

                # === Step 4: 找到 splice_byte 在 msg1 和 msg2 中对应的消息索引和偏移 ===
                # 在 msg1 中定位
                msg1_idx = 0
                while msg1_idx < len(offsets1) - 1 and offsets1[msg1_idx + 1] <= splice_byte:
                    msg1_idx += 1
                if msg1_idx >= len(msg1):
                    msg1_idx = len(msg1) - 1

                # 在 msg2 中定位
                msg2_idx = 0
                while msg2_idx < len(offsets2) - 1 and offsets2[msg2_idx + 1] <= splice_byte:
                    msg2_idx += 1
                if msg2_idx >= len(msg2):
                    msg2_idx = len(msg2) - 1

                # 计算在各自消息中的偏移
                byte_offset_in_msg1 = splice_byte - offsets1[msg1_idx] if msg1_idx < len(offsets1) - 1 else splice_byte - offsets1[-2]
                byte_offset_in_msg2 = splice_byte - offsets2[msg2_idx] if msg2_idx < len(offsets2) - 1 else splice_byte - offsets2[-2]

                # 限制偏移不越界
                byte_offset_in_msg1 = max(0, min(byte_offset_in_msg1, len(msg1[msg1_idx])))
                byte_offset_in_msg2 = max(0, min(byte_offset_in_msg2, len(msg2[msg2_idx])))

                # === Step 5: 构造新消息序列 ===
                result = []

                # 1. 添加 msg1 中拼接点之前的所有完整消息
                for i in range(msg1_idx):
                    result.append(bytearray(msg1[i]))

                # 2. 构造混合消息（msg1[msg1_idx] 前半 + msg2[msg2_idx] 后半）
                part1 = msg1[msg1_idx][:byte_offset_in_msg1]
                part2 = msg2[msg2_idx][byte_offset_in_msg2:]
                mixed_msg = bytearray(part1 + part2)
                result.append(mixed_msg)

                # 3. 添加 msg2 中拼接点之后的所有完整消息
                for i in range(msg2_idx + 1, len(msg2)):
                    result.append(bytearray(msg2[i]))

                msg1 = result


        
    
    def fuzz_one(self):
        
        if self.pending_favored:
            if self.current_test_case.was_fuzzed:
                if self.lcg.randint(0,100) < self.SKIP_TO_NEW_PROB:
                    return 1
        
        self.current_test_case.was_fuzzed = 1
        mutated_messages = copy.deepcopy(self.current_test_case.messages)
        if not mutated_messages:
            return
        indices = sorted([self.lcg.randint(0,len(mutated_messages)-1) for _ in range(2)])
        start_fuzz_msg_index, end_fuzz_msg_index = indices[0], indices[1]

        


        perf_score = self.calculate_score(self.current_test_case)

    
        stage_max = int(self.HAVOC_CYCLES_INIT * perf_score  )
        cur_stage = 0




        if self.splice:

            
            self.splice_msgs(mutated_messages)



        while cur_stage < stage_max and self.running:


            if self.splice:
                self.stats.stage_name = "splice"
            else:
                self.stats.stage_name = "havoc"

            mutation_times = self.lcg.choice([1,2,4,8,16,32,64,128])
            # 确保high > low，否则numpy会报错
            if end_fuzz_msg_index <= start_fuzz_msg_index:
                end_fuzz_msg_index = start_fuzz_msg_index + 1
            msg_indexs = np.random.randint(low=start_fuzz_msg_index, high=end_fuzz_msg_index, size=mutation_times)
            for i in range(mutation_times):
                msg_index = msg_indexs[i]

                self.mutator.mutate(mutated_messages,msg_index)

            cur_stage += 1
        



            self.common_fuzz_stuff(mutated_messages)


    @profile
    def fuzz_one_for_profile(self):
        
        
        if self.pending_favored:
            if self.current_test_case.was_fuzzed:
                if self.lcg.randint(0,100) < self.SKIP_TO_NEW_PROB:
                    return 1
        
        self.current_test_case.was_fuzzed = 1
        mutated_messages = copy.deepcopy(self.current_test_case.messages)
        start_fuzz_msg_index = self.lcg.randint(0,len(mutated_messages) - 1)
        end_fuzz_msg_index = self.lcg.randint(start_fuzz_msg_index,len(mutated_messages) )

        # print(start_fuzz_msg_index)
        # print(end_fuzz_msg_index)


        # perf_score = self.calculate_score(self.current_test_case)
        perf_score = self.HAVOC_MAX_MULT * 100
        perf_score = self.HAVOC_MAX_MULT * 100

        
        stage_max = int(self.HAVOC_CYCLES_INIT * perf_score / 10 / 100 )
        # print(stage_max)
        cur_stage = 0
        while cur_stage < stage_max:
            
            mutation_times = self.lcg.choice([1,2,4,8,16,32,64,128])
            for i in range(mutation_times):
                # for speed use lcg
                msg_index = self.lcg.randint(start_fuzz_msg_index, end_fuzz_msg_index-1)

                self.mutator.mutate(mutated_messages,msg_index)

            cur_stage += 1

        self.common_fuzz_stuff(mutated_messages)

    def fuzz_one_profile(self):
        
        profiler = LineProfiler()
        profiler.add_function(self.fuzz_one_for_profile)  # 添加要分析的函数

        # 运行分析
        profiler.enable_by_count()
        result = self.fuzz_one_for_profile()
        profiler.disable_by_count()

        # 打印结果
        profiler.print_stats(output_unit=1e-3)

    def choose_test_case(self):
        if self.current_queue_idx +1 == len(self.queue):
            self.stats.queue_cycle += 1

            # 检测这一轮是否有新的测试用例被发现
            if not self.stats.current_queued_with_cov and len(self.queue) > 10:
                self.splice = True
            else:
                self.splice = False
            
            self.stats.current_queued_with_cov = 0
            
            # 完成一轮遍历后，输出统计日志
            self._log_message_stats()

        self.current_queue_idx = (self.current_queue_idx + 1) % len(self.queue)
        self.current_test_case = self.queue[self.current_queue_idx]
        
        # 更新测试用例选择次数（Total Test）
        if hasattr(self, 'test_case_stats') and isinstance(self.current_test_case, TestCase):
            test_case_id = self._get_test_case_id(self.current_test_case)
            
            # 初始化统计
            if test_case_id not in self.test_case_stats:
                self.test_case_stats[test_case_id] = {'test_count': 0, 'new_seeds': 0, 'exec_count': 0}
            if test_case_id not in self.test_case_stats_total:
                self.test_case_stats_total[test_case_id] = {'test_count': 0, 'new_seeds': 0, 'exec_count': 0}
            
            # 增加选择次数（Total Test）
            self.test_case_stats[test_case_id]['test_count'] += 1
            self.test_case_stats_total[test_case_id]['test_count'] += 1
        
        # 更新当前测试用例中每个消息的测试次数（保留原有逻辑用于其他用途）
        if hasattr(self, 'message_stats') and isinstance(self.current_test_case, TestCase):
            test_case_id = self._get_test_case_id(self.current_test_case)
            for msg in self.current_test_case.messages:
                msg_hash = self._get_message_hash(msg)
                
                if msg_hash not in self.message_stats:
                    self.message_stats[msg_hash] = {'new_cov_count': 0, 'test_count': 0, 'total_exec_count': 0, 'test_case_id': test_case_id}
                    self.message_stats_total[msg_hash] = {'total_new_cov_count': 0, 'total_test_count': 0, 'total_exec_count': 0, 'test_case_id': test_case_id}
                else:
                    # 如果 test_case_id 未设置，则设置它
                    if 'test_case_id' not in self.message_stats[msg_hash] or not self.message_stats[msg_hash]['test_case_id']:
                        self.message_stats[msg_hash]['test_case_id'] = test_case_id
                        self.message_stats_total[msg_hash]['test_case_id'] = test_case_id
                
                self.message_stats[msg_hash]['test_count'] += 1
                # 更新该消息对应的测试用例被执行的总次数（在choose_test_case时，测试用例还未执行，所以这里先不更新）
                # total_exec_count 会在 save_if_interesting 中更新 

    def add_test_case_from_master(self):
        # 增加调用计数器
        self.master_call_counter += 1
        
        # 每10次调用才执行一次
        if self.master_call_counter % 1 != 0:
            return
            
        if self.master_dir and os.path.exists(self.master_dir):
            # 统计master_dir中所有以.raw结尾的文件
            raw_files = [os.path.join(self.master_dir, f) for f in os.listdir(self.master_dir) if f.endswith('.raw')]
            if len(raw_files) <= self.master_test_cases_id:
                return
            
            self.stats.stage_name = "master"

            repeat_times = 5
            total_iterations = len(raw_files[self.master_test_cases_id:]) * repeat_times
            
            with tqdm(total=total_iterations, desc="Processing master test cases", ncols=80) as pbar:
                for file_path in natsorted(raw_files)[self.master_test_cases_id:]:
                    for _ in range(repeat_times):
                        if self.running:
                            messages = self.read_test_case_from_file(file_path)
                            test_case = TestCase(messages=messages, file_path=file_path)
                            
                            # 检查master测试用例的md5是否已存在于集合中
                            if test_case.md5 in self.test_case_md5_set:
                                pbar.update(1)
                                continue
                            
                            fault = self.__run_target_fast(messages, self.exec_tmout)
                            self.save_if_interesting(messages, fault)
                            pbar.update(1)

            processed_count = len(raw_files) - self.master_test_cases_id
            self.master_test_cases_id = len(raw_files)
            if processed_count > 0:
                print(f"Processed {processed_count} master test cases from {self.master_dir}")



    def fuzz(self):
        print("start fuzzing, WAAAAAAAAAGH!!!")
        self.start_time = time.time()  # 记录fuzzing开始时间
        self.last_time = self.start_time
        self.last_exec = 0

        while self.running:


            self.add_test_case_from_master()

            self.choose_test_case()

            # 运行测试用例并计数
            self.fuzz_one()





    def save_if_interesting(self, messages:List[bytearray], fault, calibrate = True,name = None):
        keeping = 0 
        had_new_cov = False  # 是否发现新覆盖率
        
        if fault == FaultCode.NONE.value:
            hub = pyafl.has_new_bit()
            self.hub = hub
            if not hub:
                return 0, False  # 返回 (keeping, had_new_cov)
            if name:
                test_case_path = os.path.join(self.queue_dir,name)
            else:
                test_case_path = os.path.join(self.queue_dir,f"id:{self.stats.queue_len:06d}_{self.stats.stage_name}.raw")
            
            self.stats.queue_len += 1

            self.save_interesting_test_case(messages, test_case_path, ) 
            test_case = TestCase(messages=messages, file_path=test_case_path)
            if isinstance(self.current_test_case,TestCase):
                test_case.depth = self.current_test_case.depth + 1
            else:
                test_case.depth = 0
            
            # 将新测试用例的md5添加到集合中
            self.test_case_md5_set.add(test_case.md5)
            
            if hub == 2 and not test_case.has_new_cov:
                test_case.has_new_cov = 1
                self.stats.queued_with_cov += 1
                self.stats.current_queued_with_cov += 1
                had_new_cov = True  # 标记发现新覆盖率
                
                # 更新当前测试用例中每个消息的新覆盖率统计（保留原有逻辑用于其他用途）
                if hasattr(self, 'message_stats') and isinstance(self.current_test_case, TestCase):
                    test_case_id = self._get_test_case_id(self.current_test_case)
                    for msg in self.current_test_case.messages:
                        msg_hash = self._get_message_hash(msg)
                        
                        if msg_hash not in self.message_stats:
                            self.message_stats[msg_hash] = {'new_cov_count': 0, 'test_count': 0, 'total_exec_count': 0, 'test_case_id': test_case_id}
                            self.message_stats_total[msg_hash] = {'total_new_cov_count': 0, 'total_test_count': 0, 'total_exec_count': 0, 'test_case_id': test_case_id}
                        else:
                            # 如果 test_case_id 未设置，则设置它
                            if 'test_case_id' not in self.message_stats[msg_hash] or not self.message_stats[msg_hash]['test_case_id']:
                                self.message_stats[msg_hash]['test_case_id'] = test_case_id
                                self.message_stats_total[msg_hash]['test_case_id'] = test_case_id
                        
                        self.message_stats[msg_hash]['new_cov_count'] += 1
                        # 更新该消息对应的测试用例被执行的总次数
                        self.message_stats[msg_hash]['total_exec_count'] = self.stats.total_exec


            if calibrate:
                self.calibrate_case(test_case,self.stats.queue_cycle)

            # is_favor = self.cull_queue(test_case)

            if self.hub == 2:
                test_case_path = os.path.join(self.favor_test_cases_dir,f"id:{self.stats.unique_favors:06d}.raw")
                self.stats.unique_favors += 1

                self.save_interesting_test_case(messages, test_case_path, ) 


            self.queue.append(test_case)


            keeping = 1

        # 返回 (keeping, had_new_cov)
        return keeping, had_new_cov

        if fault == FaultCode.TMOUT.value:
            self.stats.total_tmouts += 1
            if self.stats.unique_hangs >= self.KEEP_UNIQUE_HANG:
                return keeping, False

            if  self.config['dumb_mode'] != "True":
                pyafl.simplify_trace_bits()
                if not pyafl.tmout_has_new_bit():
                    return keeping, False
            
            self.stats.unique_tmouts += 1
        
            if self.exec_tmout < self.hang_tmout:
                new_fault = self.run_target_fast(messages)
                if new_fault == FaultCode.CRASH:
                    self.save_if_interesting(messages,new_fault)
                if new_fault != FaultCode.TMOUT:
                    return keeping, False

            self.save_interesting_test_case(messages, os.path.join(self.tmout_test_cases_dir,f"id:{self.stats.unique_hangs:06d}.raw"), )

            self.stats.unique_hangs += 1
            self.stats.last_hang_time = datetime.now()


        if fault == FaultCode.CRASH.value:
            self.stats.total_crashes += 1
            if self.stats.unique_crashes >= self.KEEP_UNIQUE_CRASH:
                return keeping, False
            
            if  self.config['dumb_mode'] != "True":
                pyafl.simplify_trace_bits()
                if not pyafl.crash_has_new_bit():
                    return keeping, False

            if not self.stats.unique_crashes:
                pass

            self.save_interesting_test_case(messages, os.path.join(self.crash_test_cases_dir,f"id:{self.stats.unique_crashes:06d}.raw"), )
        
            self.stats.unique_crashes += 1

            self.stats.last_crash_time = datetime.time()
            self.stats.last_crash_execs = self.stats.total_execs
            return keeping, False

        if fault == FaultCode.ERROR.value:
            raise ValueError("Unable to execute target application")
        
        # 默认返回
        return keeping, False


        
    def save_interesting_test_case(self, messages, path):
        """
        保存interesting test case，支持本地保存和发送给master两种模式
        
        Args:
            messages: 列表，每个元素是bytes类型的协议消息
            path: 输出文件路径（本地保存模式）
            fault_type: 错误类型（发送给master模式）
            stage_name: 阶段名称（发送给master模式）
        """


        # 否则使用本地保存模式
        with open(path, 'wb') as f:
            for msg in messages:
                # 写入4字节长度（小端序）
                length = len(msg)
                f.write(length.to_bytes(4, byteorder='little', signed=False))
                # 写入原始数据
                f.write(msg)
        return True

    def read_test_case_from_file(self, file_path):
        """
        从文件中读取test case，解析保存的消息格式
        
        Args:
            file_path: 输入文件路径
            
        Returns:
            List[bytes]: 消息列表，每个元素是bytes类型的协议消息
        """
        messages = []
        
        try:
            with open(file_path, 'rb') as f:
                while True:
                    # 读取4字节长度（小端序）
                    length_bytes = f.read(4)
                    if len(length_bytes) < 4:
                        break  # 文件结束
                    
                    length = int.from_bytes(length_bytes, byteorder='little', signed=False)
                    
                    # 读取对应长度的消息数据
                    message = f.read(length)
                    if len(message) < length:
                        break  # 文件意外结束
                    
                    messages.append(bytearray(message))
                    
        except (IOError, OSError) as e:
            print(f"Error reading test case from {file_path}: {e}")
            return []
        
        return messages

    def common_fuzz_stuff(self, messages:List[bytearray]):
        
        fault = self.run_target_fast(messages, self.exec_tmout)
        
        # 更新测试用例执行次数（Total Exec）
        if hasattr(self, 'test_case_stats') and isinstance(self.current_test_case, TestCase):
            test_case_id = self._get_test_case_id(self.current_test_case)
            
            # 初始化统计（如果不存在）
            if test_case_id not in self.test_case_stats:
                self.test_case_stats[test_case_id] = {'test_count': 0, 'new_seeds': 0, 'exec_count': 0}
            if test_case_id not in self.test_case_stats_total:
                self.test_case_stats_total[test_case_id] = {'test_count': 0, 'new_seeds': 0, 'exec_count': 0}
            
            # 增加执行次数（Total Exec）
            self.test_case_stats[test_case_id]['exec_count'] += 1
            self.test_case_stats_total[test_case_id]['exec_count'] += 1

        # 调用save_if_interesting并获取返回值
        keeping, had_new_cov = self.save_if_interesting(messages, fault)
        
        # 如果发现新覆盖率，更新新种子数（Total New Seed）
        if had_new_cov and hasattr(self, 'test_case_stats') and isinstance(self.current_test_case, TestCase):
            test_case_id = self._get_test_case_id(self.current_test_case)
            
            # 确保统计已初始化
            if test_case_id not in self.test_case_stats:
                self.test_case_stats[test_case_id] = {'test_count': 0, 'new_seeds': 0, 'exec_count': 0}
            if test_case_id not in self.test_case_stats_total:
                self.test_case_stats_total[test_case_id] = {'test_count': 0, 'new_seeds': 0, 'exec_count': 0}
            
            # 增加新种子数（Total New Seed）
            self.test_case_stats[test_case_id]['new_seeds'] += 1
            self.test_case_stats_total[test_case_id]['new_seeds'] += 1
        


        
        # 计算并显示每秒执行速率
        current_time = time.time()
        elapsed = current_time - self.last_time
        
        # 每2秒更新一次统计数据
        if elapsed >= 2.0:
            # 计算当前时段的执行速度
            execs_in_period = self.stats.total_exec - self.last_exec
            self.stats.speed = execs_in_period / elapsed
            
            # 打印速率信息
            # print(f"[PERF] {execs_per_second:.1f} execs/sec | "
            #     f"Total: {self._format_count(self.stats.total_exec)} execs | tms : {self._format_count(self.tm)}")
            
            self.stats.print_stats_summary()

            # 重置计数器和时间戳
            self.last_time = current_time
            self.last_exec = self.stats.total_exec


    




    @staticmethod 
    def mr_log(messages, responses):
        """
        将已有的消息和响应记录写入日志文件
        
        参数:
            messages: 发送的消息列表
            responses: 对应的响应列表（长度应比messages多1，包含pre-run的响应）
        """
        # 验证输入
        if len(responses) != len(messages) + 1:
            raise ValueError("responses长度应为messages长度+1 (包含prerun响应)")
        
        # 写入日志文件
        with open("session.log", "w") as f:
            f.write("=== Message-Response Debug Log ===\n\n")
            
            # 记录pre-run状态
            f.write("[PRE-RUN INITIAL STATE]\n")
            f.write(f"Response: {responses[0]}\n\n")
            
            # 记录每条消息和响应
            for i, (msg, resp) in enumerate(zip(messages, responses[1:-1]), 1):
                f.write(f"[INTERACTION {i}]\n")
                f.write(f"Sent: {msg}\n")
                f.write(f"Received: {resp}\n\n")
            

            
            # 添加分隔线
            f.write("="*50 + "\n")
            f.write("Log generated at: {}\n".format(datetime.now().strftime("%Y-%m-%d %H:%M:%S")))


    # 使用示例
    @staticmethod 
    def save_pcap(messages,responses):
        # 1. 获取原始数据

        # 2. 生成PCAP文件
        pcap_gen = PcapGenerator()
        pcap_gen.save_to_pcap2(messages, responses, 'session.pcap')
        
        print(f"调试文件已保存为: session.pcap")




class MessageFuzzer(BaseFuzzer):  # ← 继承 BaseFuzzer
    def __init__(self,conf_path_or_dict):

        super().__init__(conf_path_or_dict)


        self.encrypted_messages = [
            'Finish',                    # 第一个加密的握手消息
            'ApplicationData',           # 所有应用数据
            'ClosureAlert',              # 加密关闭
            'ErrorAlert',                # 通常在加密后发送
            'KeyUpdate',                 # TLS 1.3 密钥更新
            'EndOfEarlyData',            # TLS 1.3 0-RTT 结束
            'ResumptionClientHelloAP',   # 包含加密的 ApplicationData 和 EndOfEarlyData
        ]
        
        # 所有可用的 TLS 消息列表（用于随机选择）
        self.all_messages = [
            'ClientHello',
            'ClientHelloEmptyKeyShare',  # TLS 1.3 专用
            'Certificate',
            'ClientKeyExchange',
            'CertificateVerify',
            'ChangeCipherSpec',
            'Finish',
            'ApplicationData',
            'ResumptionClientHello',
            'EmptyCertificate',
            'ClosureAlert',
            'ErrorAlert',
            'KeyUpdate',                 # TLS 1.3 密钥更新
            'EndOfEarlyData',            # TLS 1.3 0-RTT 结束
            'ResumptionClientHelloAP',   # 包含加密的 ApplicationData 和 EndOfEarlyData
        ]

        # 配置参数：每个状态生成的路径数量
        self.paths_per_state = int(self.config.get('paths_per_state', 10))

        self.generate_model()

        # 初始化状态和加密消息的索引跟踪
        self.state_list = list(self.state_paths.keys()) if self.state_paths else []
        self.current_state_idx = 0
        self.current_encrypted_msg_idx = 0
        
        # 初始化状态统计：按state统计
        # {state: {'test_count': int, 'new_seeds': int, 'exec_count': int}}
        self.state_stats = {state: {'test_count': 0, 'new_seeds': 0, 'exec_count': 0} 
                           for state in self.state_list}
        
        # 初始化累计统计：跟踪每个状态的累计统计
        # {state: {'test_count': int, 'new_seeds': int, 'exec_count': int}}
        self.state_stats_total = {state: {'test_count': 0, 'new_seeds': 0, 'exec_count': 0} 
                                 for state in self.state_list}
        
        # 上一轮的统计快照（用于计算当前轮的增量）
        self.state_stats_last_cycle = {state: {'test_count': 0, 'new_seeds': 0, 'exec_count': 0} 
                                      for state in self.state_list}
        
        # 初始化 stats.log 文件
        stats_log_path = os.path.join(self.config.get('output_dir', '.'), 'stats.log')
        self.stats_log_file = open(stats_log_path, 'a', encoding='utf-8')
        # 写入日志头（如果是新文件）
        if self.stats_log_file.tell() == 0:
            self.stats_log_file.write("=" * 80 + "\n")
            self.stats_log_file.write("MessageFuzzer State Statistics Log\n")
            self.stats_log_file.write("=" * 80 + "\n\n")
            self.stats_log_file.flush()

        # 参考 model_learning_wolfssl.py 的方式生成 SUL
        # 证书路径配置

        # 密码套件配置
        ciphersuites = [
            CipherSuite.TLS_AES_128_GCM_SHA256,
            CipherSuite.TLS_AES_256_GCM_SHA384,
            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
            CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
            CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        ]
        
        # 从配置中读取 TLS 版本，默认为 TLS 1.3
        tls_version = self.config.get('tls_version', (3, 4))
        if isinstance(tls_version, str):
            # 如果是字符串，转换为元组
            if tls_version == '1.2' or tls_version == 'tls1.2':
                tls_version = (3, 3)
            elif tls_version == '1.3' or tls_version == 'tls1.3':
                tls_version = (3, 4)
            else:
                tls_version = (3, 4)  # 默认 TLS 1.3
        
        # 从配置中读取是否需要客户端证书
        need_client_cert = self.config.get('need_client_cert', False)
        if isinstance(need_client_cert, str):
            need_client_cert = need_client_cert.lower() in ('true', '1', 'yes')
        
        # 创建 TLS 协议配置
        tlsimp = TLSProtocol(
            version=tls_version,
            cipher_suite=ciphersuites,
            implementation=self.config['implementation'],
            verify=False,
            fuzz_tls=(False, None, None),
            target=parse_net_address(self.config['use_net'])
        )
        

        # 双向认证：使用客户端证书
        keyfile = self.config.get('keyfile', None)
        certfile = self.config.get('certfile', None)
        server_cert = self.config.get('server_cert', None)
        self.sul = TLSSUT(
            TLSpro=tlsimp,
            keyfile=keyfile,
            certfile=certfile,
            server_cert=server_cert
        )


        self.stats.stage_name = "message"    

        self.s_mutation_times = 1 # 每个消息的变异次数,默认1次，随着运行时间增加，变异次数增加。
        self.s_mutation_times_max = 10 # 每个消息的变异次数最大值

        

    def generate_model(self):
        """
        从配置文件中读取 model_dir，解析 .dot 文件，生成字典：状态和对应的路径
        每个状态对应的路径生成多条（从配置文件读取 paths_per_state，默认10条）
        
        返回: Dict[str, List[List[str]]] - {state: [path1, path2, ...]}
        存储在 self.state_paths 中
        """
        # 从配置文件读取 model_dir
        if 'model_dir' not in self.config:
            self.state_paths = {}
            return
        
        model_dir = self.config['model_dir']
        if not os.path.exists(model_dir):
            print(f"警告: model_dir 不存在: {model_dir}")
            self.state_paths = {}
            return
        
        # 查找 .dot 文件（文件夹下只有一个）
        dot_file = None
        for root, dirs, files in os.walk(model_dir):
            for file in files:
                if file.endswith('.dot'):
                    dot_file = os.path.join(root, file)
                    break
            if dot_file:
                break
        
        if not dot_file:
            print(f"警告: 在 {model_dir} 中未找到 .dot 文件")
            self.state_paths = {}
            return
        

        state_paths = self._parse_dot_file(dot_file)
        
        # 转换为字典格式（每个状态只有一条最短路径）
        self.state_paths = {}
        for state, paths in state_paths.items():
            # paths 应该是一个包含单个路径的列表
            if paths and len(paths) > 0:
                self.state_paths[state] = paths  # paths 已经是 [path] 格式

        
        print(f"✓ 已加载 {len(self.state_paths)} 个状态，每个状态1条最短路径")
        if self.state_paths:
            print(f"  示例状态: {list(self.state_paths.keys())[:]}")
    
    def _parse_dot_file(self, dot_file_path):
        """
        使用 pydot 解析单个 .dot 文件，返回 {state: [shortest_path]}
        
        参数:
            dot_file_path: .dot 文件路径
            
        返回:
            Dict[str, List[List[str]]] - {state: [shortest_path]}，每个状态只有一条最短路径
        """
        graphs = pydot.graph_from_dot_file(dot_file_path)
        if not graphs:
            print(f"警告: 无法解析 .dot 文件: {dot_file_path}")
            return {}
        
        dot_graph = graphs[0]
        
        # 提取所有状态节点（排除 __start 节点）
        states = {node.get_name().strip('"') 
                  for node in dot_graph.get_nodes() 
                  if node.get_name() and not node.get_name().startswith('__start')}
        
        # 查找起始状态（从 __start 节点指向的节点）
        start_state = next((edge.get_destination().strip('"') 
                           for edge in dot_graph.get_edges() 
                           if edge.get_source().strip('"').startswith('__start')), None)
        
        if not start_state or start_state not in states:
            start_state = 's0' if 's0' in states else None
            if not start_state:
                print(f"警告: 无法确定起始状态，跳过文件 {dot_file_path}")
                return {}
        
        # 构建状态图：{from_state: [(input_msg, to_state), ...]}
        graph = defaultdict(list)
        for edge in dot_graph.get_edges():
            from_state = edge.get_source().strip('"')
            to_state = edge.get_destination().strip('"')
            
            if from_state.startswith('__start') or to_state.startswith('__start'):
                continue
            
            label = edge.get_label()
            if not label:
                continue
            
            # 提取输入消息（移除引号）
            input_msg = (label.split('/')[0] if '/' in label else label).strip().strip('"')
            if input_msg and input_msg != 'None':
                graph[from_state].append((input_msg, to_state))
        
        # 为每个状态生成最短路径
        return {state: [path] 
                for state in states 
                if (path := self._find_shortest_path_to_state(graph, start_state, state)) is not None}
        

    
    def _find_shortest_path_to_state(self, graph, start_state, target_state):
        """
        使用 BFS 找到从起始状态到目标状态的最短路径
        
        参数:
            graph: 状态图 {from_state: [(input_msg, to_state), ...]}
            start_state: 起始状态
            target_state: 目标状态
            
        返回:
            List[str] - 最短路径（消息序列），如果不存在则返回 None
        """
        if start_state == target_state:
            return []  # 返回空路径（已经在目标状态）
        
        # 使用 BFS 查找最短路径
        from collections import deque
        
        # 队列：(当前状态, 路径)
        queue = deque([(start_state, [])])
        visited = {start_state}  # 已访问的状态
        max_depth = 50  # 限制最大深度，避免无限循环
        
        depth = 0
        while queue and depth < max_depth:
            level_size = len(queue)
            for _ in range(level_size):
                current_state, path = queue.popleft()
                
                # 如果到达目标状态，返回路径
                if current_state == target_state:
                    return path
                
                # 遍历所有出边
                if current_state in graph:
                    for input_msg, next_state in graph[current_state]:
                        if next_state not in visited:
                            visited.add(next_state)
                            queue.append((next_state, path + [input_msg]))
            
            depth += 1
        
        # 如果找不到路径，返回 None
        return None



    def __get_test_cases_from_queue(self):

        self.is_resume = eval(self.config['is_resume']) if 'is_resume' in self.config else False
        
        if self.is_resume and self.queue_dir and os.path.exists(self.queue_dir):
            # 统计master_dir中所有以.raw结尾的文件
            raw_files = [os.path.join(self.queue_dir, f) for f in os.listdir(self.queue_dir) if f.endswith('.raw')]


            self.stats.queue_len = len(raw_files)

        print(f"get {len(self.queue)} test cases from queue dir {self.queue_dir}")



    def run_target_fast(self, alphabet, msg_index, timeout):

        self.stats.total_exec += 1


        pyafl.pre_run_target_without_socket(timeout)  

        # print(msg_index)
        response,sent_data,received_data = self.sul.fuzz_query(alphabet,msg_index,is_silent=True,s_mutation_times=self.s_mutation_times)

        # response,sent_data,received_data = self.sul.query_to_test(alphabet)

        # print(f"sent_data: {len(sent_data)}")
        # print(f"received_data: {len(received_data)}")

        # self.save_pcap(sent_data,received_data)

        fault = pyafl.post_run_target_without_socket(timeout)

        return fault,sent_data


    def fuzz_one(self):
        
        # 根据状态统计动态计算 stage_max
        stage_max = self._calculate_stage_max_for_state() 
        stage_max = int(stage_max )
        cur_stage = 0
        while cur_stage < stage_max and self.running:


            cur_stage += 1
        
            self.common_fuzz_stuff(self.current_test_case,self.current_encrypted_msg_idx)

            pass



    def common_fuzz_stuff(self,alphabet:List[str],msg_index:int):

        fault,sent_data = self.run_target_fast(alphabet,msg_index,self.exec_tmout)

        # 更新状态执行次数（Total Exec）
        if hasattr(self, 'state_list') and self.state_list and hasattr(self, 'state_stats'):
            current_state = self.state_list[self.current_state_idx]
            if current_state in self.state_stats:
                self.state_stats[current_state]['exec_count'] += 1
                self.state_stats_total[current_state]['exec_count'] += 1

        # 保存结果并检查是否有新覆盖率
        keeping, had_new_cov = self.save_if_interesting(sent_data,fault,calibrate = False)
        
        # 如果发现新覆盖率，更新状态的新种子数（Total New Seed）
        if had_new_cov and hasattr(self, 'state_list') and self.state_list and hasattr(self, 'state_stats'):
            current_state = self.state_list[self.current_state_idx]
            if current_state in self.state_stats:
                self.state_stats[current_state]['new_seeds'] += 1
                self.state_stats_total[current_state]['new_seeds'] += 1

        # 计算并显示每秒执行速率
        current_time = time.time()
        elapsed = current_time - self.last_time
        # print(f"elapsed: {elapsed}")
        # 每2秒更新一次统计数据
        if elapsed >= 2.0:
            # 计算当前时段的执行速度
            execs_in_period = self.stats.total_exec - self.last_exec
            self.stats.speed = execs_in_period / elapsed
            
            # 打印速率信息
            # print(f"[PERF] {execs_per_second:.1f} execs/sec | "
            #     f"Total: {self._format_count(self.stats.total_exec)} execs | tms : {self._format_count(self.tm)}")
            
            self.stats.print_stats_summary()

            self.last_time = current_time
            self.last_exec = self.stats.total_exec

    def _calculate_stage_max_for_state(self):
        """
        根据状态统计动态计算 stage_max
        规则：
        1. 新覆盖率越多，stage_max 越大
        2. 测试次数越多，stage_max 越小（但比例要小于上一个策略）
        """
        base_stage_max = self.HAVOC_MAX_MULT * 100
        
        # 如果没有状态统计，使用默认值
        if not hasattr(self, 'state_list') or not self.state_list or not hasattr(self, 'state_stats'):
            return base_stage_max
        
        current_state = self.state_list[self.current_state_idx]
        
        if current_state not in self.state_stats:
            return base_stage_max
        
        state_stat = self.state_stats[current_state]
        # 兼容旧的数据结构，如果字段不存在则使用默认值
        new_seeds = state_stat.get('new_seeds', state_stat.get('new_cov_count', 0))
        test_count = state_stat.get('test_count', 0)
        
        # 计算新种子因子（新种子越多，因子越大）
        # 使用对数函数避免因子过大，max_factor = 2.0 表示最多2倍
        if new_seeds > 0:
            # 新种子因子：1.0 + log(1 + new_seeds) / log(10) * 0.5
            # 当 new_seeds = 1 时，因子 ≈ 1.15
            # 当 new_seeds = 10 时，因子 ≈ 1.5
            # 当 new_seeds = 100 时，因子 ≈ 1.75
            import math
            cov_factor = 1.0 + math.log(1 + new_seeds) / math.log(10) * 0.5
            cov_factor = min(cov_factor, 2.0)  # 限制最大为2.0
        else:
            cov_factor = 1.0
        
        # 计算测试次数因子（测试次数越多，因子越小，但比例要小于覆盖率因子）
        # 使用平方根函数，影响较小
        if test_count > 0:
            # 测试次数因子：1.0 / (1 + sqrt(test_count) / 10)
            # 当 test_count = 1 时，因子 ≈ 0.91
            # 当 test_count = 100 时，因子 ≈ 0.5
            # 当 test_count = 1000 时，因子 ≈ 0.24
            import math
            test_factor = 1.0 / (1 + math.sqrt(test_count) / 10)
            test_factor = max(test_factor, 0.2)  # 限制最小为0.2
        else:
            test_factor = 1.0
        
        # 组合两个因子
        # 覆盖率因子影响更大（直接相乘），测试次数因子影响较小（作为衰减）
        stage_max = int(base_stage_max * cov_factor * test_factor)
        
        # 确保 stage_max 在合理范围内
        min_stage_max = base_stage_max // 5  # 最小为 base 的 1/5
        max_stage_max = base_stage_max * 3   # 最大为 base 的 3倍
        stage_max = max(min_stage_max, min(stage_max, max_stage_max))
        
        return stage_max

    def choose_test_case(self):
        """
        新策略：先选择状态，然后 test_case = 状态路径 + 遍历的加密消息
        """
        # 如果没有状态，使用默认行为
        if not self.state_list or not self.encrypted_messages:
            # 如果没有状态或加密消息，使用空列表作为默认测试用例
            self.current_test_case = []
            return
        
        # 获取当前状态
        current_state = self.state_list[self.current_state_idx]
        
        # 更新状态选择次数（Total Test）
        if hasattr(self, 'state_stats') and current_state in self.state_stats:
            self.state_stats[current_state]['test_count'] += 1
            self.state_stats_total[current_state]['test_count'] += 1
        
        # 获取到达该状态的路径（每个状态只有一条最短路径）
        state_path = self.state_paths[current_state][0] if current_state in self.state_paths and self.state_paths[current_state] else []
        
        # 获取当前加密消息
        current_encrypted_msg = self.encrypted_messages[self.current_encrypted_msg_idx]
        
        # 检查状态或加密消息是否改变，只在改变时打印
        if not hasattr(self, '_last_state_idx') or not hasattr(self, '_last_encrypted_msg_idx'):
            self._last_state_idx = -1
            self._last_encrypted_msg_idx = -1
        
        state_changed = (self._last_state_idx != self.current_state_idx)
        msg_changed = (self._last_encrypted_msg_idx != self.current_encrypted_msg_idx)
        
        # 组合测试用例：状态路径 + 加密消息
        self.current_test_case = state_path + [current_encrypted_msg]
        self.current_id_to_fuzz = len(state_path)
        
        # 从所有消息中随机添加三个消息
        if hasattr(self, 'all_messages') and self.all_messages and len(self.all_messages) > 0:
            # 从所有消息列表中随机选择3个（允许重复）
            random_messages = random.choices(self.all_messages, k=3)
            self.current_test_case = self.current_test_case + random_messages 

        # 只在状态或加密消息改变时打印
        if state_changed or msg_changed:
            print(f"current_test_case: {self.current_test_case}", flush=True)
            self._last_state_idx = self.current_state_idx
            self._last_encrypted_msg_idx = self.current_encrypted_msg_idx
        
        # 更新索引：先遍历加密消息，再切换到下一个状态
        self.current_encrypted_msg_idx += 1
        
        # 如果已遍历完所有加密消息，切换到下一个状态
        if self.current_encrypted_msg_idx >= len(self.encrypted_messages):
            self.current_encrypted_msg_idx = 0
            self.current_state_idx = (self.current_state_idx + 1) % len(self.state_list)
            
            # 如果完成了一轮所有状态的遍历，增加队列周期计数并输出统计日志
            if self.current_state_idx == 0:
                self.stats.queue_cycle += 1
                self._log_state_stats()

    def _log_state_stats(self):
        """
        输出每个状态的统计信息到 stats.log 文件
        包括：1. 当前周期的统计 2. 累计统计
        使用state_stats字典，按state统计
        """
        if not hasattr(self, 'stats_log_file') or not hasattr(self, 'state_stats'):
            return
        
        try:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.stats_log_file.write(f"\n[Cycle {self.stats.queue_cycle}] Statistics at {timestamp}\n")
            self.stats_log_file.write(f"Total Executions: {self.stats.total_exec}\n")
            self.stats_log_file.write("=" * 80 + "\n")
            
            # 获取所有状态
            all_states = set()
            if hasattr(self, 'state_list') and self.state_list:
                all_states = set(self.state_list)
            # 从state_stats中获取已有的state
            for state in self.state_stats.keys():
                all_states.add(state)
            
            # ========== 当前周期的统计 ==========
            self.stats_log_file.write("\n--- Current Cycle Statistics ---\n")
            self.stats_log_file.write("-" * 90 + "\n")
            self.stats_log_file.write(f"{'State':<30} {'Total Tests':<15} {'Total New Seeds':<15} {'Total Exec':<15}\n")
            self.stats_log_file.write("-" * 90 + "\n")
            
            # 计算当前周期的增量（相对于上一轮）
            cycle_stats = {}
            for state in all_states:
                current_stats = self.state_stats.get(state, {'test_count': 0, 'new_seeds': 0, 'exec_count': 0})
                last_stats = self.state_stats_last_cycle.get(state, {'test_count': 0, 'new_seeds': 0, 'exec_count': 0})
                
                # 计算增量，如果为负数则设为0（说明该状态在新周期中还没有活动）
                test_count_diff = current_stats['test_count'] - last_stats['test_count']
                new_seeds_diff = current_stats['new_seeds'] - last_stats['new_seeds']
                exec_count_diff = current_stats['exec_count'] - last_stats['exec_count']
                
                cycle_stats[state] = {
                    'test_count': max(0, test_count_diff),
                    'new_seeds': max(0, new_seeds_diff),
                    'exec_count': max(0, exec_count_diff)
                }
            
            # 按state排序输出当前周期统计
            total_cycle_test = 0
            total_cycle_new_seeds = 0
            total_cycle_exec = 0
            
            for state in sorted(cycle_stats.keys()):
                stats = cycle_stats[state]
                exec_str = self._format_exec_count(stats['exec_count'])
                self.stats_log_file.write(
                    f"{state:<30} {stats['test_count']:<15} {stats['new_seeds']:<15} {exec_str:<15}\n"
                )
                total_cycle_test += stats['test_count']
                total_cycle_new_seeds += stats['new_seeds']
                total_cycle_exec += stats['exec_count']
            
            # 当前周期总计
            self.stats_log_file.write("-" * 90 + "\n")
            exec_str = self._format_exec_count(total_cycle_exec)
            self.stats_log_file.write(
                f"{'CYCLE TOTAL':<30} {total_cycle_test:<15} {total_cycle_new_seeds:<15} {exec_str:<15}\n"
            )
            
            # ========== 累计统计 ==========
            self.stats_log_file.write("\n--- Total Statistics (All Cycles) ---\n")
            self.stats_log_file.write("-" * 90 + "\n")
            self.stats_log_file.write(f"{'State':<30} {'Total Tests':<15} {'Total New Seeds':<15} {'Total Exec':<15}\n")
            self.stats_log_file.write("-" * 90 + "\n")
            
            # 按state排序输出累计统计
            total_all_test = 0
            total_all_new_seeds = 0
            total_all_exec = 0
            
            for state in sorted(all_states):
                stats = self.state_stats_total.get(state, {'test_count': 0, 'new_seeds': 0, 'exec_count': 0})
                exec_str = self._format_exec_count(stats['exec_count'])
                self.stats_log_file.write(
                    f"{state:<30} {stats['test_count']:<15} {stats['new_seeds']:<15} {exec_str:<15}\n"
                )
                total_all_test += stats['test_count']
                total_all_new_seeds += stats['new_seeds']
                total_all_exec += stats['exec_count']
            
            # 累计总计
            self.stats_log_file.write("-" * 90 + "\n")
            exec_str = self._format_exec_count(total_all_exec)
            self.stats_log_file.write(
                f"{'TOTAL (ALL)':<30} {total_all_test:<15} {total_all_new_seeds:<15} {exec_str:<15}\n"
            )
            
            # ========== 总体统计摘要 ==========
            self.stats_log_file.write("\n--- Overall Summary ---\n")
            self.stats_log_file.write("-" * 100 + "\n")
            self.stats_log_file.write(f"Total Executions: {self.stats.total_exec}\n")
            self.stats_log_file.write(f"Total Queue Size: {self.stats.queue_len}\n")
            self.stats_log_file.write(f"Total States: {len(all_states)}\n")
            self.stats_log_file.write(f"Total Test Count (All States): {total_all_test}\n")
            self.stats_log_file.write(f"Total New Seeds (All States): {total_all_new_seeds}\n")
            self.stats_log_file.write("-" * 100 + "\n")
            
            self.stats_log_file.write("=" * 80 + "\n")
            self.stats_log_file.flush()  # 确保立即写入文件
            
            # 更新上一轮的统计快照
            # 对于所有状态，更新快照（包括那些在新周期中没有活动的状态）
            for state in all_states:
                current_stats = self.state_stats.get(state, {'test_count': 0, 'new_seeds': 0, 'exec_count': 0})
                self.state_stats_last_cycle[state] = {
                    'test_count': current_stats['test_count'],
                    'new_seeds': current_stats['new_seeds'],
                    'exec_count': current_stats['exec_count']
                }
            
        except Exception as e:
            print(f"Warning: Failed to write stats log: {e}")
    
    def close_stats_log(self):
        """
        关闭 stats.log 文件
        """
        if hasattr(self, 'stats_log_file') and self.stats_log_file:
            try:
                # 检查文件是否已经关闭
                if not self.stats_log_file.closed:
                    # 写入结束标记
                    self.stats_log_file.write("\n" + "=" * 80 + "\n")
                    self.stats_log_file.write("Fuzzing session ended.\n")
                    self.stats_log_file.write("=" * 80 + "\n")
                    self.stats_log_file.flush()
                    self.stats_log_file.close()
            except (ValueError, AttributeError) as e:
                # 文件已经关闭或无效，忽略错误
                pass
            except Exception as e:
                print(f"Warning: Failed to close stats log: {e}")
    
    def __del__(self):
        """
        析构函数，确保 stats.log 文件被正确关闭
        """
        try:
            self.close_stats_log()
        except:
            pass
        # 调用父类的析构函数
        try:
            super().__del__()
        except:
            pass








class MasterFuzzer(BaseFuzzer):
    """
    MasterFuzzer类用于管理多个fuzzer实例
    
    主要功能：
    1. 解析主配置文件
    2. 校验slaves配置
    3. 将主配置转换为basefuzzer和messagefuzzer能够接收的格式
    4. 自动添加use_net字段
    5. 替换target_cmd中的@@占位符为实际端口
    6. 为每种fuzzer类型分配独立的端口和输出目录
    
    使用示例：
        # 创建MasterFuzzer实例
        master = MasterFuzzer('/path/to/config.json')
        
        # 查看转换后的配置
        master.print_fuzzer_configs()
        
        # 保存所有fuzzer配置到文件
        config_paths = master.save_all_fuzzer_configs()
        
        # 获取通信处理器
        comm_handler = master.get_communication_handler()
        
        # 创建BaseFuzzer实例，启用通信
        basefuzzer = BaseFuzzer(
            conf_path='/path/to/basefuzzer_config.json',
            comm=comm_handler,
            fuzzer_id='basefuzzer_0'
        )
        
        # 创建MessageFuzzer实例，启用通信
        messagefuzzer = MessageFuzzer(
            conf_path='/path/to/messagefuzzer_config.json',
            comm=comm_handler,
            fuzzer_id='messagefuzzer_0'
        )
        
        # MasterFuzzer处理接收到的interesting test cases
        processed_count = master.process_interesting_test_cases(timeout=1.0)
    """
    def __init__(self, conf_path_or_dict):
        # 先解析配置，但不调用父类的 __init__
        if isinstance(conf_path_or_dict, dict):
            self.config = conf_path_or_dict
        else:
            with open(conf_path_or_dict, 'r', encoding='utf-8') as f:
                self.config = json.load(f)

        self.slaves_queue = dict()


        # 处理 MasterFuzzer 特有的配置
        self.slaves = self.config['slaves']
        self.fuzzer_type = ['basefuzzer', 'messagefuzzer']

        self.start_port = self.config['start_port']
        # 校验 slaves 配置
        self.validate_slaves_config()

        self.base_fuzzer_num = self.slaves['basefuzzer'] if 'basefuzzer' in self.slaves else 0
        self.message_fuzzer_num = self.slaves['messagefuzzer'] if 'messagefuzzer' in self.slaves else 0
        
        # 生成所有fuzzer配置
        self.fuzzer_configs = self.generate_fuzzer_configs()
        # 保存所有 fuzzer 配置文件
        self._save_fuzzer_configs()
        

        self.received_test_cases = []  # 存储接收到的interesting test cases
        
        # 进程管理相关
        self.slave_processes = {}  # 存储各个fuzzer的进程信息
        
        # 为 MasterFuzzer 创建一个简化的配置用于初始化
        master_config = self._create_master_config()
        
        # 现在调用父类的 __init__，传入简化的配置
        super().__init__(master_config)

        # 设置重复运行次数
        self.repeat_times = 1
        
        # 扫描未测试testcase相关变量
        self.last_scan_time = time.time()
        self.scan_interval = 10  # 每10秒扫描一次
        self.total_untested = 0

        self.current_slave_fuzzer = None
        

    def format_duration(self, seconds):
        """将秒数格式化为日时分秒格式"""
        days = int(seconds // 86400)
        hours = int((seconds % 86400) // 3600)
        minutes = int((seconds % 3600) // 60)
        secs = int(seconds % 60)



        return f"{days} days {hours:02d} h {minutes:02d} m {secs:02d} s"


        # if days > 0:
        #     return f"{days}d{hours:02d}h{minutes:02d}m{secs:02d}s"
        # elif hours > 0:
        #     return f"{hours:02d}h{minutes:02d}m{secs:02d}s"
        # elif minutes > 0:
        #     return f"{minutes:02d}m{secs:02d}s"
        # else:
        #     return f"{secs:02d}s"

    def scan_untested_testcases(self):
        """扫描所有fuzzer下还没有测试过的testcase"""
        current_time = time.time()
        if current_time - self.last_scan_time >= self.scan_interval:
            self.last_scan_time = current_time
            
            self.total_untested = 0
            for fuzzer_name, fuzzer_config in self.slaves_queue.items():
                # 检查queue目录
                queue_dir = fuzzer_config.get('queue_dir')
                if queue_dir and os.path.exists(queue_dir):
                    queue_files = [f for f in os.listdir(queue_dir) if f.endswith('.raw')]
                    untested_in_queue = len(queue_files) - fuzzer_config.get('len_queue', 0)
                    self.total_untested += untested_in_queue

                

    def handle_interrupt(self, signum, frame):
        print("\n[!] 检测到中断信号，正在停止...")
        self.running = False
        self.stop_slaves()
        
        sys.exit(0)




    def _create_master_config(self):
        """为 MasterFuzzer 创建简化的配置"""
        # 从原始配置中提取基本配置，移除 slaves 相关配置
        master_config = self.config.copy()
        
        # 移除 slaves 配置，因为这是 MasterFuzzer 特有的
        if 'slaves' in master_config:
            del master_config['slaves']
        
        # 设置 MasterFuzzer 不是 slave
        master_config['is_slave'] = "False"
        
        # 设置 MasterFuzzer 的网络配置（使用端口 4431）
        master_config['use_net'] = "tcp://127.0.0.1/4431"
        
        # 更新 target_cmd 中的端口
        if 'target_cmd' in master_config:
            if isinstance(master_config['target_cmd'], list):
                # 如果是列表，使用第一个元素并替换端口
                if master_config['target_cmd']:
                    master_config['target_cmd'] = master_config['target_cmd'][0].replace('@@', '4431')
                else:
                    del master_config['target_cmd']
            else:
                # 如果是字符串，直接替换
                master_config['target_cmd'] = master_config['target_cmd'].replace('@@', '4431')
        
        # 处理 model_dir 数组：如果是列表，使用第一个元素
        if 'model_dir' in master_config and isinstance(master_config['model_dir'], list):
            if master_config['model_dir']:
                master_config['model_dir'] = master_config['model_dir'][0]
            else:
                del master_config['model_dir']
        
        # 处理 tls_version 数组：如果是列表，使用第一个元素
        if 'tls_version' in master_config and isinstance(master_config['tls_version'], list):
            if master_config['tls_version']:
                master_config['tls_version'] = master_config['tls_version'][0]
            else:
                del master_config['tls_version']
        
        # 设置 MasterFuzzer 的输出目录
        original_output_dir = master_config.get('output_dir', '/tmp/fuzzer_output')
        master_config['output_dir'] = f"{original_output_dir}/master"
        
        return master_config

    def fuzz(self, test_seeds=False):
        print("start fuzzing, WAAAAAAAAAGH!!!")
        self.last_exec = 0
        
        # 启动所有slave fuzzer进程（使用独立进程模式）
        self.start_slaves_detached()
        

        if test_seeds:
            self.test_seeds()
        # 等待slaves启动完成
        time.sleep(3)
        
        # 初始化实时显示相关变量
        self.last_display_time = time.time()
        self.display_interval = self.config.get('display_interval', 1)  # 从配置读取显示间隔，
        self.enable_realtime_display = self.config.get('enable_realtime_display', True)  # 是否启用实时显示，默认启用
        self.start_time = time.time()

        try:
            while self.running:

                
                self.add_test_case_from_slaves()
                

        except KeyboardInterrupt:
            print("\n收到中断信号，正在停止...")
        except Exception as e:
            print(f"\n发生错误: {e}")
        finally:
            # 停止所有slave fuzzer进程
            self.stop_slaves()
            print("所有进程已停止")

    def display_realtime_stats(self):
        """显示实时统计信息，支持清屏刷新"""
        import os
        
        # 清屏
        os.system('clear' if os.name == 'posix' else 'cls')

        elapsed_time = time.time() - self.start_time
        
        processed_queue_len = sum(config.get('len_queue', 0) for config in self.slaves_queue.values())

        # 获取所有 fuzzer 进程状态
        slave_status = self.get_slave_status()
        running_count = sum(1 for status in slave_status.values() if status['status'] == 'running')
        stopped_count = sum(1 for status in slave_status.values() if status['status'] == 'stopped')
        unknown_count = sum(1 for status in slave_status.values() if status['status'] == 'unknown')
        total_fuzzers = len(slave_status)
        
        # 格式化 fuzzer 状态显示
        status_parts = []
        if running_count > 0:
            status_parts.append(f"[green]{running_count}[/green] running")
        if stopped_count > 0:
            status_parts.append(f"[red]{stopped_count}[/red] stopped")
        if unknown_count > 0:
            status_parts.append(f"[yellow]{unknown_count}[/yellow] unknown")
        
        if status_parts:
            status_text = ", ".join(status_parts) + f" (total: {total_fuzzers})"
        else:
            status_text = f"total: {total_fuzzers}"

        # 创建实时统计面板
        panel_content = (
            f"[cyan]{utils.center_text('running time', 20)}:[/cyan] {utils.center_text(self.format_duration(elapsed_time), 20)}\n"
            f"[cyan]{utils.center_text('fuzzers status', 20)}:[/cyan] {utils.center_text(status_text, 20)}\n"
            f"[cyan]{utils.center_text('processed test cases', 20)}:[/cyan] {utils.center_text(f'{utils.format_count(processed_queue_len)}([green]{self.stats.queue_len}[/green])', 20)}\n"
            f"[cyan]{utils.center_text('favor', 20)}:[/cyan] {utils.center_text(str(self.stats.unique_favors), 20)}\n"
            f"[cyan]{utils.center_text('speed', 20)}:[/cyan] {utils.center_text(f'{self.stats.speed:.1f}', 20)}\n"
            f"[cyan]{utils.center_text('total exec', 20)}:[/cyan] {utils.center_text(str(utils.format_count(self.stats.total_exec)), 20)}\n"
            f"[cyan]{utils.center_text('waiting', 20)}:[/cyan] {utils.center_text(str(self.total_untested if self.total_untested != 0 else 'no new testcases'), 20)}\n"

        )
        
        # 检查是否需要合并显示
        fuzzer_count = len(self.slaves_queue)
        if fuzzer_count > 15:
            # 当fuzzer数量超过15个时，按类型分别合并显示
            panel_content += f"\n\n  [yellow]detecting {fuzzer_count} parallel Fuzzer，merge by type[/yellow]\n\n\n\n"
            # 按类型分组fuzzer
            base_fuzzers = {}
            message_fuzzers = {}
            
            for fuzzer_name, config in self.slaves_queue.items():
                if 'base' in fuzzer_name.lower():
                    base_fuzzers[fuzzer_name] = config
                elif 'message' in fuzzer_name.lower():
                    message_fuzzers[fuzzer_name] = config
            
            # 显示BaseFuzzer统计
            if base_fuzzers:
                panel_content += f"  [bold green]{utils.center_text(f'BaseFuzzers({len(base_fuzzers)}):', 22)}[/bold green]"
                base_queue = sum(config.get('len_queue', 0) for config in base_fuzzers.values())
                base_unique = sum(config.get('len_unique_queue', 0) for config in base_fuzzers.values())
                base_favor = sum(config.get('len_favor_queue', 0) for config in base_fuzzers.values())
                base_crash = sum(config.get('len_crash_test_cases', 0) for config in base_fuzzers.values())
                base_timeout = sum(config.get('len_tmout_test_cases', 0) for config in base_fuzzers.values())

                

                
                # 使用utils.center_text进行居中对齐，标签和内容一起居中
                queue_centered = utils.center_text(f"Queue:{utils.format_count(base_queue)}({utils.format_count(base_unique)})", 20)
                favor_centered = utils.center_text(f"Favor:{utils.format_count(base_favor)}", 12)
                crash_centered = utils.center_text(f"Crash:{base_crash}", 12)
                timeout_centered = utils.center_text(f"Timeout:{base_timeout}", 15)
                
                panel_content += (
                    f" | [cyan][bold yellow]{queue_centered}[/bold yellow][/cyan] "
                    f"| [magenta][bold magenta]{favor_centered}[/bold magenta][/magenta] "
                    f"| [red][bold red]{crash_centered}[/bold red][/red] "
                    f"| [orange1][bold orange1]{timeout_centered}[/bold orange1][/orange1]\n"
                )
            
            # 显示MessageFuzzer统计
            if message_fuzzers:
                panel_content += f"\n  [bold blue]{utils.center_text(f'MessageFuzzers({len(message_fuzzers)}):', 22)}[/bold blue]"
                message_queue = sum(config.get('len_queue', 0) for config in message_fuzzers.values())
                message_unique = sum(config.get('len_unique_queue', 0) for config in message_fuzzers.values())
                message_favor = sum(config.get('len_favor_queue', 0) for config in message_fuzzers.values())
                message_crash = sum(config.get('len_crash_test_cases', 0) for config in message_fuzzers.values())
                message_timeout = sum(config.get('len_tmout_test_cases', 0) for config in message_fuzzers.values())

            
                
                # 使用utils.center_text进行居中对齐，标签和内容一起居中
                queue_centered = utils.center_text(f"Queue:{utils.format_count(message_queue)}({utils.format_count(message_unique)})", 20)
                favor_centered = utils.center_text(f"Favor:{utils.format_count(message_favor)}", 12)
                crash_centered = utils.center_text(f"Crash:{message_crash}", 12)
                timeout_centered = utils.center_text(f"Timeout:{message_timeout}", 15)
                
                panel_content += (
                    f" | [cyan][bold yellow]{queue_centered}[/bold yellow][/cyan] "
                    f"| [magenta][bold magenta]{favor_centered}[/bold magenta][/magenta] "
                    f"| [red][bold red]{crash_centered}[/bold red][/red] "
                    f"| [orange1][bold orange1]{timeout_centered}[/bold orange1][/orange1]\n"
                )
            
            # 显示总体统计
            total_queue = sum(config.get('len_queue', 0) for config in self.slaves_queue.values())
            total_unique = sum(config.get('len_unique_queue', 0) for config in self.slaves_queue.values())
            total_favor = sum(config.get('len_favor_queue', 0) for config in self.slaves_queue.values())
            total_crash = sum(config.get('len_crash_test_cases', 0) for config in self.slaves_queue.values())
            total_timeout = sum(config.get('len_tmout_test_cases', 0) for config in self.slaves_queue.values())
            
            # 使用utils.center_text进行居中对齐，标签和内容一起居中
            queue_centered = utils.center_text(f"Queue:{utils.format_count(total_queue)}({utils.format_count(total_unique)})", 20)
            favor_centered = utils.center_text(f"Favor:{utils.format_count(total_favor)}", 12)
            crash_centered = utils.center_text(f"Crash:{total_crash}", 12)
            timeout_centered = utils.center_text(f"Timeout:{total_timeout}", 15)
            
            # 使用utils函数居中对齐all标签
            all_centered = utils.center_text("all:", 22)
            panel_content += f"\n  [bold yellow]{all_centered}[/bold yellow]"
            panel_content += (
                f" | [cyan][bold yellow]{queue_centered}[/bold yellow][/cyan] "
                f"| [magenta][bold magenta]{favor_centered}[/bold magenta][/magenta] "
                f"| [red][bold red]{crash_centered}[/bold red][/red] "
                f"| [orange1][bold orange1]{timeout_centered}[/bold orange1][/orange1]\n"
            )
        else:
            # 当fuzzer数量不超过10个时，显示所有详细信息
            for fuzzer_name, config in self.slaves_queue.items():
                last_update = config.get('last_update_time')
                queue_count = config.get('len_queue', 0)
                unique_queue_count = config.get('len_unique_queue', 0)
                favor_count = config.get('len_favor_queue', 0)
                crash_count = config.get('len_crash_test_cases', 0)
                timeout_count = config.get('len_tmout_test_cases', 0)
                
                if last_update:
                    time_diff = time.time() - last_update.timestamp()
                    time_diff_str = humanize.naturaldelta(time_diff)
                else:
                    time_diff_str = "never"
                
                # 使用utils.center_text进行居中对齐，标签和内容一起居中
                fuzzer_centered = utils.center_text(f"{fuzzer_name}:", 20)
                queue_centered = utils.center_text(f"Queue:{utils.format_count(queue_count)}({utils.format_count(unique_queue_count)})", 20)
                favor_centered = utils.center_text(f"Favor:{utils.format_count(favor_count)}", 12)
                crash_centered = utils.center_text(f"Crash:{crash_count}", 12)
                timeout_centered = utils.center_text(f"Timeout:{timeout_count}", 15)
                
                panel_content += (
                    f"  [bold magenta]{fuzzer_centered}[/bold magenta] "
                    f"| [cyan][bold yellow]{queue_centered}[/bold yellow][/cyan] "
                    f"| [magenta][bold magenta]{favor_centered}[/bold magenta][/magenta] "
                    f"| [red][bold red]{crash_centered}[/bold red][/red] "
                    f"| [orange1][bold orange1]{timeout_centered}[/bold orange1][/orange1] "
                    f"([dim]{time_diff_str}[/dim])\n"
                )
        
        current_fuzzer = getattr(self.stats, 'stage_name', '未知')
        panel_content += f"\n[bold blue]Status :[/bold blue] scanning {self.current_slave_fuzzer}... ({self.display_interval}s)"
        
        panel = Panel(
            panel_content.strip(),
            title="[bold blue]Master Fuzzer[/bold blue]",
            border_style="green",
            padding=(1, 2)
        )
        
        console = Console()
        console.print(panel)

    def display_slaves_stats(self, fuzzer_name, new_cases_count, case_type, len_queue):
        """显示从slaves获取测试用例的统计信息"""
        current_time = datetime.now()
        time_str = current_time.strftime("%Y-%m-%d %H:%M:%S")
        
        # 获取当前fuzzer的配置
        fuzzer_config = self.slaves_queue.get(fuzzer_name, {})
        last_update = fuzzer_config.get('last_update_time')
        len_queue = fuzzer_config.get('len_queue', 0)
        
        # 计算时间间隔
        if last_update:
            time_diff = current_time - last_update
            time_diff_str = humanize.naturaldelta(time_diff)
        else:
            time_diff_str = "首次获取"
        
        # 创建统计表格
        table = Table(title=f"从 {fuzzer_name} 获取测试用例统计", box=ROUNDED)
        table.add_column("项目", style="cyan")
        table.add_column("数值", style="green")
        
        table.add_row("当前时间", time_str)
        table.add_row("上次获取时间", time_diff_str)
        table.add_row("本次获取数量", str(new_cases_count))
        table.add_row("测试用例类型", case_type)
        table.add_row("累计处理总数", str(len_queue))
        
        # 显示表格
        console = Console()
        console.print(table)
        print()  # 添加空行分隔

    def display_overall_stats(self, total_new_cases):
        """显示总体统计信息"""
        current_time = datetime.now()
        time_str = current_time.strftime("%Y-%m-%d %H:%M:%S")
        
        # 计算所有fuzzer的总处理数量和unique_queue总数
        len_queue = sum(config.get('len_queue', 0) for config in self.slaves_queue.values())
        total_unique_queue = sum(config.get('len_unique_queue', 0) for config in self.slaves_queue.values())
        
        # 创建总体统计面板
        panel_content = f"""
[bold green]总体统计信息[/bold green]

[cyan]当前时间:[/cyan] {time_str}
[cyan]本次获取总数:[/cyan] {total_new_cases}
[cyan]累计处理总数:[/cyan] {len_queue}
[cyan]Unique Queue总数:[/cyan] {total_unique_queue}

[bold yellow]各Fuzzer统计:[/bold yellow]
"""
        
        for fuzzer_name, config in self.slaves_queue.items():

            unique_queue_count = config.get('len_unique_queue', 0)
            last_update = config.get('last_update_time')
            if last_update:
                time_diff = current_time - last_update
                time_diff_str = humanize.naturaldelta(time_diff)
            else:
                time_diff_str = "从未获取"
            
            panel_content += f"  [green]{fuzzer_name}:[/green] {config.get('len_queue', 0)} 个测试用例 | Unique: {unique_queue_count} (上次: {time_diff_str})\n"
        
        panel = Panel(
            panel_content.strip(),
            title="[bold blue]Master Fuzzer 测试用例获取统计[/bold blue]",
            border_style="blue",
            padding=(1, 2)
        )
        
        console = Console()
        console.print(panel)
        print()  # 添加空行分隔

    def add_test_case_from_slaves(self):
        """从slaves获取测试用例并显示统计信息"""


        for fuzzer_name, fuzzer_config in self.slaves_queue.items():
            fuzzer_new_cases = 0
            
            # 处理queue
            queue_dir = fuzzer_config['queue_dir']
            if queue_dir and os.path.exists(queue_dir):
                self.stats.stage_name = fuzzer_name
                raw_files = [os.path.join(queue_dir, f) for f in os.listdir(queue_dir) if f.endswith('.raw')]
                queue_len = copy.deepcopy(fuzzer_config['len_queue'])

                self.total_untested = len(raw_files) - queue_len
                self.current_slave_fuzzer = fuzzer_name
                self.stats.stage_name = fuzzer_name
                
                for file_path in natsorted(raw_files)[queue_len:]:
                    if "master" not in file_path:

                        messages = self.read_test_case_from_file(file_path)
                        test_case = TestCase(file_path, messages)
                        
                        if test_case.md5 not in self.test_case_md5_set:
                            
                            self.test_case_md5_set.add(test_case.md5)

                            for run_count in range(self.repeat_times):
                                fault = self.run_target_fast(messages, self.exec_tmout)
                                self.save_if_interesting(messages, fault, calibrate = False)


                                if self.hub > 0:
                                    fuzzer_config['len_unique_queue'] += 1
                                if self.hub == 2:
                                    fuzzer_config['len_favor_queue'] += 1

                                fuzzer_new_cases += 1
                
                    fuzzer_config['len_queue'] += 1
                    self.total_untested -= 1

                    # 定期显示实时统计信息
                    if self.enable_realtime_display:
                        current_time = time.time()
                        if current_time - self.last_display_time >= self.display_interval:
                            execs_in_period = self.stats.total_exec - self.last_exec
                            self.stats.speed = execs_in_period / (current_time - self.last_display_time)
                            self.last_exec = self.stats.total_exec
                            self.last_display_time = current_time

                            self.display_realtime_stats()
                        



            # 定期显示实时统计信息
            if self.enable_realtime_display:
                current_time = time.time()
                if current_time - self.last_display_time >= self.display_interval:
                    execs_in_period = self.stats.total_exec - self.last_exec
                    self.stats.speed = execs_in_period / (current_time - self.last_display_time)
                    self.last_exec = self.stats.total_exec
                    self.last_display_time = current_time

                    self.display_realtime_stats()

            # 处理crash_test_cases
            crash_test_cases_dir = fuzzer_config['crash_test_cases_dir']
            if crash_test_cases_dir and os.path.exists(crash_test_cases_dir):

                raw_files = [os.path.join(crash_test_cases_dir, f) for f in os.listdir(crash_test_cases_dir) if f.endswith('.raw')]
                len_crash_test_cases = copy.deepcopy(fuzzer_config['len_crash_test_cases'])

                
                for file_path in natsorted(raw_files)[len_crash_test_cases:]:
                    messages = self.read_test_case_from_file(file_path)
                    fault = self.run_target_fast(messages, self.exec_tmout)
                    self.save_if_interesting(messages, fault, calibrate = False)
                    fuzzer_config['len_crash_test_cases'] += 1
                    fuzzer_new_cases += 1

                


            # 处理tmout_test_cases
            tmout_test_cases_dir = fuzzer_config['tmout_test_cases_dir']
            if tmout_test_cases_dir and os.path.exists(tmout_test_cases_dir):

                raw_files = [os.path.join(tmout_test_cases_dir, f) for f in os.listdir(tmout_test_cases_dir) if f.endswith('.raw')]
                len_tmout_test_cases = copy.deepcopy(fuzzer_config['len_tmout_test_cases'])

                
                for file_path in natsorted(raw_files)[len_tmout_test_cases:]:
                    messages = self.read_test_case_from_file(file_path)
                    fault = self.run_target_fast(messages, self.exec_tmout)
                    self.save_if_interesting(messages, fault, calibrate = False)
                    fuzzer_config['len_tmout_test_cases'] += 1
                    fuzzer_new_cases += 1

                

            
            # 更新最后更新时间
            if fuzzer_new_cases > 0:
                fuzzer_config['last_update_time'] = datetime.now()
        










    def _save_fuzzer_configs(self):
        """保存所有 fuzzer 的配置文件到 /tmp 目录"""
        import json
        config_parent_dir = self.config['config_parent_dir']
        os.makedirs(config_parent_dir,exist_ok=True)
        # 保存 BaseFuzzer 配置
        for i in range(self.base_fuzzer_num):
            config_path = f"{config_parent_dir}/basefuzzer_{i}_config.json"
            if i < len(self.fuzzer_configs.get('basefuzzer', [])):
                with open(config_path, 'w', encoding='utf-8') as f:
                    json.dump(self.fuzzer_configs['basefuzzer'][i], f, indent=2, ensure_ascii=False)
                print(f"保存 BaseFuzzer_{i} 配置到: {config_path}")
        
        # 保存 MessageFuzzer 配置
        for i in range(self.message_fuzzer_num):
            config_path = f"{config_parent_dir}/messagefuzzer_{i}_config.json"
            if i < len(self.fuzzer_configs.get('messagefuzzer', [])):
                with open(config_path, 'w', encoding='utf-8') as f:
                    json.dump(self.fuzzer_configs['messagefuzzer'][i], f, indent=2, ensure_ascii=False)
                print(f"保存 MessageFuzzer_{i} 配置到: {config_path}")


    def validate_slaves_config(self):
        """
        校验 slaves 配置中的键是否都在 fuzzer_type 列表中
        
        Raises:
            ValueError: 如果发现无效的 fuzzer 类型
        """
        if not self.slaves:
            print("警告: slaves 配置为空")
            return
            
        # 将 fuzzer_type 转换为小写以便比较
        valid_types = [fuzzer_type.lower() for fuzzer_type in self.fuzzer_type]
        
        # 检查每个 slave 的键
        invalid_types = []
        for slave_type in self.slaves.keys():
            if slave_type.lower() not in valid_types:
                invalid_types.append(slave_type)
        
        if invalid_types:
            error_msg = f"发现无效的 fuzzer 类型: {invalid_types}. 有效的类型包括: {self.fuzzer_type}"
            raise ValueError(error_msg)
        
        print(f"✓ slaves 配置校验通过: {list(self.slaves.keys())}")
    
    def generate_fuzzer_configs(self):
        """
        将主配置转换为basefuzzer和messagefuzzer能够接收的格式
        
        Returns:
            dict: 包含basefuzzer和messagefuzzer配置的字典
        """
        fuzzer_configs = {
            'basefuzzer': [],
            'messagefuzzer': []
        }
        
        # 从主配置中提取网络信息
        # 如果配置中有use_net字段，解析它来获取IP和端口

        
        current_port = self.start_port+1
        ip = self.config['ip']
        
        # 计算全局fuzzer索引（用于从数组中分配配置）
        global_fuzzer_index = 0
        
        # 为每种fuzzer类型生成配置
        fuzzer_types = [
            ('basefuzzer', self.base_fuzzer_num),
            ('messagefuzzer', self.message_fuzzer_num)
        ]
        
        for fuzzer_type, fuzzer_count in fuzzer_types:
            for i in range(fuzzer_count):
                config = self.config.copy()
                fuzzer_name = f"{fuzzer_type}_{i}"
                # 添加use_net字段
                config['use_net'] = f"tcp://{ip}/{current_port}"
                config['is_slave'] = "True"
                config['is_resume'] = "True"

                # 处理数组配置：model_dir, target_cmd, tls_version
                # 如果这些字段是数组，使用全局索引来选择对应的元素
                if 'model_dir' in config and isinstance(config['model_dir'], list):
                    if global_fuzzer_index < len(config['model_dir']):
                        config['model_dir'] = config['model_dir'][global_fuzzer_index]
                    else:
                        # 如果索引超出范围，使用最后一个元素或第一个元素
                        config['model_dir'] = config['model_dir'][-1] if config['model_dir'] else None
                
                if 'target_cmd' in config:
                    if isinstance(config['target_cmd'], list):
                        # 如果是数组，先选择对应的元素
                        if global_fuzzer_index < len(config['target_cmd']):
                            target_cmd = config['target_cmd'][global_fuzzer_index]
                        else:
                            target_cmd = config['target_cmd'][-1] if config['target_cmd'] else None
                        # 然后替换@@占位符
                        if target_cmd:
                            config['target_cmd'] = target_cmd.replace('@@', str(current_port))
                    else:
                        # 如果不是数组，直接替换@@占位符
                        config['target_cmd'] = config['target_cmd'].replace('@@', str(current_port))
                
                if 'tls_version' in config and isinstance(config['tls_version'], list):
                    if global_fuzzer_index < len(config['tls_version']):
                        config['tls_version'] = config['tls_version'][global_fuzzer_index]
                    else:
                        config['tls_version'] = config['tls_version'][-1] if config['tls_version'] else None
                
                # 为每个fuzzer创建独立的输出目录
                original_output_dir = config.get('output_dir', '/tmp/fuzzer_output')
                config['output_dir'] = f"{original_output_dir}/{fuzzer_name}"
                config["master_dir"] = f"{original_output_dir}/master"

                
                self.slaves_queue[fuzzer_name] = {
                    "fuzzer_name": fuzzer_name,
                    "queue_dir": f"{config['output_dir']}/queue",
                    "crash_test_cases_dir": f"{config['output_dir']}/crash_test_cases",
                    "tmout_test_cases_dir": f"{config['output_dir']}/tmout_test_cases",
                    "len_queue": 0,
                    "len_crash_test_cases": 0,
                    "len_tmout_test_cases": 0,
                    "len_unique_queue": 0,
                    "len_favor_queue": 0,
                    "last_update_time": None,
                    "total_exec_test_cases": 0,
                }

                del_list = ['ip', 'master_port', 'config_parent_dir', 'start_port', 'is_resume', 'is_slave','slaves','seeds_dir']
                for key in del_list:
                    if key in config:
                        del config[key]
                
                # 移除slaves配置，因为这是主配置特有的
                if 'slaves' in config:
                    del config['slaves']
                
                
                fuzzer_configs[fuzzer_type].append(config)
                current_port += 1
                global_fuzzer_index += 1
            
        return fuzzer_configs
    
    def save_fuzzer_config(self, config, fuzzer_type, index):
        """
        保存单个fuzzer配置到文件
        
        Args:
            config (dict): fuzzer配置
            fuzzer_type (str): fuzzer类型 ('basefuzzer' 或 'messagefuzzer')
            index (int): fuzzer索引
            
        Returns:
            str: 保存的配置文件路径
        """
        import os
        
        # 创建配置文件名
        config_dir = os.path.dirname(self.conf_path)
        config_name = os.path.splitext(os.path.basename(self.conf_path))[0]
        config_filename = f"{config_name}_{fuzzer_type}_{index}.json"
        config_path = os.path.join(config_dir, config_filename)
        
        # 保存配置到文件
        with open(config_path, 'w', encoding='utf-8') as f:
            json.dump(config, f, indent=4, ensure_ascii=False)
        
        print(f"✓ {fuzzer_type.capitalize()} {index} 配置已保存到: {config_path}")
        return config_path
    
    def save_all_fuzzer_configs(self):
        """
        保存所有fuzzer配置到文件
        
        Returns:
            dict: 包含所有配置文件路径的字典
        """
        config_paths = {
            'basefuzzer': [],
            'messagefuzzer': []
        }
        
        # 保存basefuzzer配置
        for i, config in enumerate(self.fuzzer_configs['basefuzzer']):
            config_path = self.save_fuzzer_config(config, 'basefuzzer', i)
            config_paths['basefuzzer'].append(config_path)
        
        # 保存messagefuzzer配置
        for i, config in enumerate(self.fuzzer_configs['messagefuzzer']):
            config_path = self.save_fuzzer_config(config, 'messagefuzzer', i)
            config_paths['messagefuzzer'].append(config_path)
        
        return config_paths
    
    def print_fuzzer_configs(self):
        """
        打印所有fuzzer配置信息，用于调试和验证
        """
        print(f"\n=== Fuzzer 配置信息 ===")
        print(f"BaseFuzzer数量: {len(self.fuzzer_configs['basefuzzer'])}")
        print(f"MessageFuzzer数量: {len(self.fuzzer_configs['messagefuzzer'])}")
        
        # 打印BaseFuzzer配置
        for i, config in enumerate(self.fuzzer_configs['basefuzzer']):
            print(f"\n--- BaseFuzzer {i} ---")
            print(f"use_net: {config.get('use_net', 'N/A')}")
            print(f"target_cmd: {config.get('target_cmd', 'N/A')}")
            print(f"output_dir: {config.get('output_dir', 'N/A')}")
            print(f"name: {config.get('name', 'N/A')}")
        
        # 打印MessageFuzzer配置
        for i, config in enumerate(self.fuzzer_configs['messagefuzzer']):
            print(f"\n--- MessageFuzzer {i} ---")
            print(f"use_net: {config.get('use_net', 'N/A')}")
            print(f"target_cmd: {config.get('target_cmd', 'N/A')}")
            print(f"output_dir: {config.get('output_dir', 'N/A')}")
            print(f"name: {config.get('name', 'N/A')}")
        
        print("=" * 30)
    
    def start_slaves(self):
        """
        启动所有slave fuzzer进程
        
        启动命令格式: python3 ./basefuzzer.py config_path
        或: python3 ./messagefuzzer.py config_path
        
        使用独立进程模式，进程不会因为父进程退出而终止
        """
        import subprocess
        import os
        import signal
        
        print("正在启动所有slave fuzzer进程...")
        
        # 获取配置文件目录
        config_parent_dir = self.config['config_parent_dir']
        
        # 启动BaseFuzzer进程
        for i in range(self.base_fuzzer_num):
            fuzzer_name = f"basefuzzer_{i}"
            config_path = f"{config_parent_dir}/basefuzzer_{i}_config.json"
            
            if os.path.exists(config_path):
                try:
                    # 启动独立进程（detach=True 使进程独立于父进程）
                    process = subprocess.Popen(
                        ['python3', './basefuzzer.py', config_path],
                        stdout=subprocess.DEVNULL,
                        stderr=subprocess.DEVNULL,
                        stdin=subprocess.DEVNULL,
                        start_new_session=True,  # 创建新的会话
                        preexec_fn=os.setsid if hasattr(os, 'setsid') else None  # 在Unix系统上创建新的进程组
                    )
                    
                    # 存储进程信息
                    self.slave_processes[fuzzer_name] = {
                        'process': process,
                        'config_path': config_path,
                        'fuzzer_type': 'basefuzzer',
                        'start_time': time.time()
                    }
                    
                    print(f"✓ 启动 {fuzzer_name} (PID: {process.pid})")
                    
                except Exception as e:
                    print(f"✗ 启动 {fuzzer_name} 失败: {e}")
            else:
                print(f"✗ 配置文件不存在: {config_path}")
        
        # 启动MessageFuzzer进程
        for i in range(self.message_fuzzer_num):
            fuzzer_name = f"messagefuzzer_{i}"
            config_path = f"{config_parent_dir}/messagefuzzer_{i}_config.json"
            
            if os.path.exists(config_path):
                try:
                    # 启动独立进程（detach=True 使进程独立于父进程）
                    process = subprocess.Popen(
                        ['python3', './messagefuzzer.py', config_path],
                        stdout=subprocess.DEVNULL,
                        stderr=subprocess.DEVNULL,
                        stdin=subprocess.DEVNULL,
                        start_new_session=True,  # 创建新的会话
                        preexec_fn=os.setsid if hasattr(os, 'setsid') else None  # 在Unix系统上创建新的进程组
                    )
                    
                    # 存储进程信息
                    self.slave_processes[fuzzer_name] = {
                        'process': process,
                        'config_path': config_path,
                        'fuzzer_type': 'messagefuzzer',
                        'start_time': time.time()
                    }
                    
                    print(f"✓ 启动 {fuzzer_name} (PID: {process.pid})")
                    
                except Exception as e:
                    print(f"✗ 启动 {fuzzer_name} 失败: {e}")
            else:
                print(f"✗ 配置文件不存在: {config_path}")
        
        print(f"总共启动了 {len(self.slave_processes)} 个slave fuzzer进程")
    
    def start_slaves_detached(self):
        """
        启动完全独立的slave fuzzer进程（使用nohup）
        
        这些进程将完全独立于父进程，即使父进程退出也不会影响子进程
        """
        import subprocess
        import os
        import signal
        
        print("正在启动完全独立的slave fuzzer进程...")
        
        # 获取配置文件目录
        config_parent_dir = self.config['config_parent_dir']
        
        # 创建日志目录
        log_dir = f"{config_parent_dir}/logs"
        os.makedirs(log_dir, exist_ok=True)
        
        # 启动BaseFuzzer进程
        for i in range(self.base_fuzzer_num):
            fuzzer_name = f"basefuzzer_{i}"
            config_path = f"{config_parent_dir}/basefuzzer_{i}_config.json"
            log_file = f"{log_dir}/{fuzzer_name}.log"
            
            if os.path.exists(config_path):
                try:
                    # 使用nohup启动完全独立的进程
                    cmd = f"nohup python3 ./basefuzzer.py {config_path} > {log_file} 2>&1 &"
                    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
                    
                    if result.returncode == 0:
                        # 获取进程PID（从nohup输出中提取）
                        try:
                            # 等待一下让进程启动
                            time.sleep(0.5)
                            # 通过进程名查找PID
                            try:
                                import psutil
                                for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
                                    try:
                                        if proc.info['cmdline'] and 'basefuzzer.py' in ' '.join(proc.info['cmdline']) and config_path in ' '.join(proc.info['cmdline']):
                                            pid = proc.info['pid']
                                            break
                                    except (psutil.NoSuchProcess, psutil.AccessDenied):
                                        continue
                                else:
                                    pid = "未知"
                            except ImportError:
                                # 如果没有psutil，使用pgrep命令
                                import subprocess
                                try:
                                    result = subprocess.run(['pgrep', '-f', f'basefuzzer.py.*{config_path}'], 
                                                          capture_output=True, text=True)
                                    if result.returncode == 0 and result.stdout.strip():
                                        pid = int(result.stdout.strip().split('\n')[0])
                                    else:
                                        pid = "未知"
                                except:
                                    pid = "未知"
                            
                            # 存储进程信息
                            self.slave_processes[fuzzer_name] = {
                                'process': None,  # 独立进程没有process对象
                                'pid': pid,
                                'config_path': config_path,
                                'fuzzer_type': 'basefuzzer',
                                'start_time': time.time(),
                                'log_file': log_file,
                                'is_detached': True
                            }
                            
                            print(f"✓ 启动独立进程 {fuzzer_name} (PID: {pid}, 日志: {log_file})")
                            
                        except ImportError:
                            # 如果没有psutil，使用简单的方法
                            self.slave_processes[fuzzer_name] = {
                                'process': None,
                                'pid': "未知",
                                'config_path': config_path,
                                'fuzzer_type': 'basefuzzer',
                                'start_time': time.time(),
                                'log_file': log_file,
                                'is_detached': True
                            }
                            print(f"✓ 启动独立进程 {fuzzer_name} (日志: {log_file})")
                    
                except Exception as e:
                    print(f"✗ 启动 {fuzzer_name} 失败: {e}")
            else:
                print(f"✗ 配置文件不存在: {config_path}")
        
        # 启动MessageFuzzer进程
        for i in range(self.message_fuzzer_num):
            fuzzer_name = f"messagefuzzer_{i}"
            config_path = f"{config_parent_dir}/messagefuzzer_{i}_config.json"
            log_file = f"{log_dir}/{fuzzer_name}.log"
            
            if os.path.exists(config_path):
                try:
                    # 使用nohup启动完全独立的进程
                    cmd = f"nohup python3 ./messagefuzzer.py {config_path} > {log_file} 2>&1 &"
                    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
                    
                    if result.returncode == 0:
                        # 获取进程PID
                        try:
                            time.sleep(0.5)
                            try:
                                import psutil
                                for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
                                    try:
                                        if proc.info['cmdline'] and 'messagefuzzer.py' in ' '.join(proc.info['cmdline']) and config_path in ' '.join(proc.info['cmdline']):
                                            pid = proc.info['pid']
                                            break
                                    except (psutil.NoSuchProcess, psutil.AccessDenied):
                                        continue
                                else:
                                    pid = "未知"
                            except ImportError:
                                # 如果没有psutil，使用pgrep命令
                                import subprocess
                                try:
                                    result = subprocess.run(['pgrep', '-f', f'messagefuzzer.py.*{config_path}'], 
                                                          capture_output=True, text=True)
                                    if result.returncode == 0 and result.stdout.strip():
                                        pid = int(result.stdout.strip().split('\n')[0])
                                    else:
                                        pid = "未知"
                                except:
                                    pid = "未知"
                            
                            # 存储进程信息
                            self.slave_processes[fuzzer_name] = {
                                'process': None,
                                'pid': pid,
                                'config_path': config_path,
                                'fuzzer_type': 'messagefuzzer',
                                'start_time': time.time(),
                                'log_file': log_file,
                                'is_detached': True
                            }
                            
                            print(f"✓ 启动独立进程 {fuzzer_name} (PID: {pid}, 日志: {log_file})")
                            
                        except ImportError:
                            self.slave_processes[fuzzer_name] = {
                                'process': None,
                                'pid': "未知",
                                'config_path': config_path,
                                'fuzzer_type': 'messagefuzzer',
                                'start_time': time.time(),
                                'log_file': log_file,
                                'is_detached': True
                            }
                            print(f"✓ 启动独立进程 {fuzzer_name} (日志: {log_file})")
                    
                except Exception as e:
                    print(f"✗ 启动 {fuzzer_name} 失败: {e}")
            else:
                print(f"✗ 配置文件不存在: {config_path}")
        
        print(f"总共启动了 {len(self.slave_processes)} 个独立slave fuzzer进程")
    
    def get_child_processes(self, parent_pid):
        """
        递归获取指定进程的所有子进程
        
        Args:
            parent_pid (int): 父进程PID
            
        Returns:
            list: 所有子进程PID列表
        """
        child_pids = []
        try:
            # 使用ps命令获取子进程
            result = subprocess.run(['ps', '-o', 'pid,ppid', '--no-headers'], 
                                  capture_output=True, text=True, timeout=5)
            if result.returncode == 0:
                lines = result.stdout.strip().split('\n')
                for line in lines:
                    if line.strip():
                        parts = line.strip().split()
                        if len(parts) >= 2:
                            pid, ppid = int(parts[0]), int(parts[1])
                            if ppid == parent_pid:
                                child_pids.append(pid)
                                # 递归获取子进程的子进程
                                child_pids.extend(self.get_child_processes(pid))
        except:
            pass
        return child_pids

    def stop_slaves(self):
        """
        强制停止所有slave fuzzer进程及其子进程
        """
        print("正在强制停止所有slave fuzzer进程及其子进程...")
        time.sleep(2)
        # 收集所有需要停止的PID（包括子进程）
        pids_to_kill = []
        
        for fuzzer_name, process_info in self.slave_processes.items():
            try:
                if process_info.get('is_detached', False):
                    # 处理独立进程
                    pid = process_info.get('pid')
                    if pid and pid != "未知":
                        pids_to_kill.append(pid)
                        # 获取并添加所有子进程
                        child_pids = self.get_child_processes(pid)
                        pids_to_kill.extend(child_pids)
                else:
                    # 处理普通子进程
                    process = process_info['process']
                    if process.poll() is None:  # 进程仍在运行
                        pids_to_kill.append(process.pid)
                        # 获取并添加所有子进程
                        child_pids = self.get_child_processes(process.pid)
                        pids_to_kill.extend(child_pids)
            except:
                pass
        
        # 去重
        pids_to_kill = list(set(pids_to_kill))
        
        # 批量强制杀死所有进程
        stopped_count = 0
        for pid in pids_to_kill:
            try:
                os.kill(pid, signal.SIGKILL)
                stopped_count += 1
            except ProcessLookupError:
                stopped_count += 1
            except:
                pass
        
        # 清空进程字典
        self.slave_processes.clear()


        os.system(f"kill -9 $(pgrep {self.config['implementation'].lower()})")
        
        print(f"总共停止了 {stopped_count} 个进程（包括子进程）")
    
    def get_slave_status(self):
        """
        获取所有slave fuzzer进程的状态
        
        Returns:
            dict: 包含各个fuzzer进程状态的字典
        """
        import os
        status = {}
        
        for fuzzer_name, process_info in self.slave_processes.items():
            process = process_info.get('process')
            pid = process_info.get('pid')
            is_detached = process_info.get('is_detached', False)
            
            # 如果是独立进程（process为None），通过PID检查进程状态
            if is_detached or process is None:
                if pid == "未知" or pid is None:
                    # PID未知，无法检查状态
                    status[fuzzer_name] = {
                        'status': 'unknown',
                        'pid': pid,
                        'uptime': time.time() - process_info['start_time']
                    }
                else:
                    # 通过PID检查进程是否存在
                    try:
                        # 使用os.kill(pid, 0)检查进程是否存在（不发送信号）
                        os.kill(pid, 0)
                        # 进程仍在运行
                        status[fuzzer_name] = {
                            'status': 'running',
                            'pid': pid,
                            'uptime': time.time() - process_info['start_time']
                        }
                    except (ProcessLookupError, OSError):
                        # 进程不存在
                        status[fuzzer_name] = {
                            'status': 'stopped',
                            'pid': pid,
                            'uptime': time.time() - process_info['start_time']
                        }
            else:
                # 普通进程，使用process对象检查状态
                if process.poll() is None:
                    # 进程仍在运行
                    status[fuzzer_name] = {
                        'status': 'running',
                        'pid': process.pid,
                        'uptime': time.time() - process_info['start_time']
                    }
                else:
                    # 进程已结束
                    status[fuzzer_name] = {
                        'status': 'stopped',
                        'pid': process.pid,
                        'exit_code': process.returncode,
                        'uptime': time.time() - process_info['start_time']
                    }
        
        return status
    

if __name__ == "__main__":
    master = MasterFuzzer("configs/openssl_paral.json")

    master.fuzz()
    # master.print_fuzzer_configs()
    # master.save_all_fuzzer_configs()